Hacker News new | past | comments | ask | show | jobs | submit login
OCaml at Bloomberg (twitter.com/adolfont)
175 points by yawaramin 5 months ago | hide | past | favorite | 138 comments

I'd really like to know the negatives of this approach.

I want to introduce F# to my workplace. I have the authority to do it but lack the experience to back it up. I don't want to cost the company mega dollars down the road so I don't want to make a bad call. But to me F# leads to a more reliable solution. It's like programming with training wheels. You can setup the types to make it very hard for programming mistakes to slip in. Of course you can also have a style that lets about anything fly. With the right approach I feel more confident that my solutions will by robust and less buggy when using F#.

As someone who has a lot of time for OCaml - I'm honestly not so big on F# anymore.

You end up having to use .NET Objects in F# because their modules are so limited compared to OCamls. And at that point I figure why not just use C# - which is a lot more expressive than it used to be - and have a lot less friction using the .NET ecosystem?

I have a fair amount of experience in both F# and Ocaml and don't think the lack of modules harms F#. The trick I had to learn was to think of F# as its own language and not try to write it as Ocaml or Haskell. What I do sometimes miss in F# are existential types.

I also wouldn't say objects in F# are less natural. This is only opinion but, I prefer F#'s lighter object syntax and its object expressions are great. Active patterns also provide an expressive option for dispatching on abstract datatypes. The conflicts vs .NET I've found with F# OOP are when implementing comparison operators, or when interacting with frameworks that want public fields. Other than that, there's not much friction. I suppose there is friction when it comes to MS tooling for producing Windows UIs but then, F# also has Fable and Elm inspired UI kits for various platforms. Going AOT route will also meet with some friction.

Comparing languages comes down to what problem solving approaches they make difficult by default. In that light, F# is much more properly a "functional" language than C#. The language is designed from the ground up for recursive algebraic data structures, HM type inference, flexible pattern destructuring, null avoidance, exhaustive matching, computation expressions, idioms around composable higher order functions, clean syntax for function types, immutability and sound type guided development.

It also has a light python like syntax and like all MLs but perhaps even more so, is pragmatic when it comes to the use of esoteric design concepts. The philosophy is not to be functional and pure for their own sake but about composing language defaults which force certain types of correctness promoting approaches.

F# also has units of measure and type providers. They're probably less often used. But just the json, html and xml providers are useful on their own.

To be clear, this isn't arguing F# vs C#. Such arguments are extremely boring. It's emphasizing what core design decisions distinguish the two languages.

As someone who's used both I generally am not as big of a fan of OcAML where it matters - usage in most companies and projects. Sure it has some features that are nice but often they are "nice to have's", whereas the F# has things that IMO are more necessary for day to day development. Basic things like multi-threading last time I checked weren't as ideal (particularly for FP languages where this can shine); and the library ecosystem in F# basically takes in all of .NET reducing the risk of adoption substantially. At least my team will never be stuck inter-operating with some product, and can keep up with latest trends.

What's important are minimising the risk of adoption and also allow FP to show its value since that's what will make the adoption successful. I don't want my dev's to be stuck when adopting the new technology for a project because there is a library missing.

On the C# side depends on the problem space. If your working in a space where FP can help shave dev time substantially then F# is the better choice IMO. C# is not bad, but F# shines in certain domains in terms of dev time in my experience.

> Basic things like multi-threading last time I checked weren't as ideal

For most line-of-business/CRUD/APIs/etc. multi-threading isn't really a must-have. Otherwise why are so many people doing just fine with Python/Ruby/Node? OCaml has really good concurrency support (check the OP, they serve hundreds of millions of requests/day with extremely low latency–quoting directly from their slide here).

> At least my team will never be stuck inter-operating with some product,

Valid point, but it highly depends what you're using it for. I would say for like 90% of web-ish use cases the current OCaml ecosystem would work just fine.

> and can keep up with latest trends.

That's not necessarily a positive. Our job is to deliver business value, not follow the latest fashionable trends.

>That's not necessarily a positive. Our job is to deliver business value, not follow the latest fashionable trends.

Just to clarify latest trends don't mean untried tech, it sometimes means just new features of existing products making it into your ecosystem. Some examples I can think of include an AWS SDK adding an option to to their SDK's for a well used product that suits your project, better well-tested support for a message bus library used by your company, a vendor product mandated by your organisation providing only mainstream language SDK's (Node, Java, .NET), etc. Yes I could make my own SDK's in OcAML (I like the lang btw) but then I'm not "delivering business value" as per your comment. It also increases the risk of project failure using the time budget allocated - in the team's I've seen using FP this would of been a real risk.

> "Basic things like multi-threading"

In my time I've seen many projects switch from Node/Python APIs to Java/.NET/C++/etc when they become high scale backends just to take advantage of the better performance envelope. Java/.NET usually is chosen if multi-threading is appropriate. Seen this happen a lot and its pretty common. Seen savings up to 200k+ USD a year for just one component where going multi-threaded helped. Its quite common once a business reaches a certain scale to desire multi-threading for a number of business cases.

> For most line-of-business/CRUD/APIs/etc.

For those standard CRUD API's you mention unfortunately the ability to use Ocaml, F#, or any other functional language often is reduced unless you are doing it already for other value reasons - its hard to justify the value add over say Java, C# or even Node. The app probably isn't doing too much anyway other than calling a database and serialising the result to the client.

It's when you add handle data in those API's or data processes, add algorithms to them (e.g. processing engines, etc), handle data pipelines that FP IMO shows its true business value, and those cases often benefit from multi-threading and/or shared immutable state. F# does well at that, has good async support, and most of the FP goodies (strong typing, type inference, etc), and has an acceptable performance (.NET is pretty good these days).

In the end my point is I think F# right now IMO is much "less risky" out of the two to adopt for most corps especially if you are doing data transformation, pipelines, and APIs (given ASP.NET Core's speed these days) especially with the broader library support. YMMV of course and this is a general opinionated statement - if those adoption risks don't apply to what you are developing great.

> Yes I could make my own SDK's in OcAML (I like the lang btw) but then I'm not "delivering business value" as per your comment.

Sure, fair–OCaml is not all things to all people. Use as and if appropriate, after having researched the requirements and the available libraries. E.g. see OP–Bloomberg–who found appropriate use cases.

> It also increases the risk of project failure using the time budget allocated

If a software project is being developed on a 'time budget' I have my doubts that it's being run the right way. Software project estimates are mostly useless in my experience, and they usually end up taking however long they take, after stressing everyone out needlessly with meetings and email about why it's taking so long.

> in the team's I've seen using FP this would of been a real risk.

Or it may have ultimately been a better choice, because of correctness and maintainability benefits which kick in after the project has ramped up. I've mentioned this elsewhere in the thread as well. It's hard to make a decisive argument like this based on only a few points of experience. For every experience you have, someone else may have the opposite experience.

> Its quite common once a business reaches a certain scale to desire multi-threading for a number of business cases.

Then why were they written in Node/Python in the first place? Because they were cheaper and faster to develop than they would have been as Java/.NET/C++ projects, and allowed the project to actually survive so that it could face the challenges of scaling up. What you are talking about here is basically survivorship bias.

IMHO, what OCaml offers is a middle ground, a development pace slightly slower than Node/Python, but much faster and safer than the heavy-duty typed OOP languages, and with performance benefits that may mean that you never need to worry about migrating to something else for scalability. Again, see OP.

> its hard to justify the value add over say Java, C# or even Node.

It's hard to justify the value add if the project is being run by non-engineers/MBAs/etc., it's easy to justify if run by experienced engineers who get the bug-squashing benefits of OCaml's typed FP and iteration speed.

> F#

F# is a great language. Just like OCaml, it has its place in certain areas e.g. in Windows/C# shops that are looking for something better.

I like OcAML as well - in fact I stated that. The original reply was to a comment comparing OcAML to F# in the context of adoption in a company to a risk adverse poster worried about costing the company "mega dollars", where one reply dismissed F# due to .NET interop. My point was really that's also the reason why IMO it is less risky to adopt for many corporations for a person such as this - there's less chance to be blocked by some missing thing. Awkward but workable on the edge cases beats having to do more work for many projects in the real world we live in.

F# isn't that risky of a language to adopt and even to non-engineer management types as you say - it's .NET. To most of them, they wouldn't know the difference so in that context it is "less risky" for that poster.

I replied with my opinion that for many enterprises (not all) looking to move into FP, F# would seem a less drastic change with many of the benefits (library interoperability/ecosystem size, threading, etc). That doesn't apply to the use cases in this article which is great/fantastic - I'm all for that!

I actually agree with most of your points, but adoption is just as much about convincing others as it is technical qualities, maybe even more so. Getting people onto the journey and making them comfortable with the risk is important.

I would argue with .NET Core F# more has an equal place on the server side (Linux even) given the problems it is more geared to solving. They are both good languages, one is based on the other after all with slightly different tradeoffs. Both OcAML and F# are more similar than different IMO.

If we are talking about adoption and name recognition among non-engineers (despite this being a very poor way to run software engineering projects), then unless you are already a .Net shop I see no compelling reason to adopt it over JVM and (if typed FP is desired) Scala. Everyone knows Java, and Scala probably has at least twice the adoption levels of F# (pulling numbers out of thin air here).

I think there are factors to consider one over the other; just like sometimes OcAML could be a good choice as well. I wouldn't dismiss F# for production use however having seen it used successfully on large things in Windows and Linux environments.

> talking about adoption and name recognition among non-engineers

Agree that non-engineer recognition it is a poor way to run projects but sadly that matters to some engineers I've talked to as well + comfort zones. Having F#/Scala experience means they could always find a C#/Java job and that depends on the city/industry you are in as an example.

> I see no compelling reason to adopt it over JVM

That's an opinion preferring the JVM over the CLR which is a stating potentially a personal preference? Technically both are capable so I think that's a moot point - the CLR is pretty good and Java on some things is playing catchup (e.g. value types). JVM is good too but the differences won't probably mean much to most projects.

> Scala probably has at least twice the adoption levels of F#

Scala has many good points and some use cases better than F# for sure. However to play the other side some reasons I can think of on the F# side include that default web performance favors ASP.NET Core (even on Linux) from bench marking by quite a bit over say Spring Boot/Play. The HM type inference also is a plus for F# IMO (as it is for OcAML) and it leans to being more FP first giving the FP benefits quicker with less fuss. The multi threading (Futures/Async-Await) API seems more ubiquitous in the .NET space last time I checked, less LOC to use, and is exposed on a lot more libraries/clients/etc as a first class citizen. Its also IMO a lot easier to make your whole program async and has more inbuilt language support to avoid overhead. Important things in hosting an API I think especially around thread use/scalability. F# has things like value types, compile time generics, tail recursion (for FP), Spans, and other CLR features etc which allow that low level performance tuning where required - it seems to be closer to the CLR than say Scala is to the JVM. Something I do like, just like C#, is to compile it to self packaged bundles for multiple targets (ARM, MacOS, Windows, etc) in a smaller bundle than JVM based apps.

Popularity is a proxy for risk. I would assert it only matters IF the base isn't mature and it doesn't have an ecosystem to piggy back on. Popularity is a proxy measure for "something's probably missing" which for say Scala/F# isn't really the case given their ecosystems depending on your use case. OcAML is probably less popular than F# as an example but for certain use cases could still work - sadly it doesn't have that ecosystem to piggy back of and that is a factor for some teams (not all but many I've been in). F#'s been around for awhile and reasonably mature as a base.

Scala is a good language too. But I wouldn't dismiss F# in these comparisons either. There's nuances that need to be considered. Where you want a language with the crisp FP first feel/benefits of OcAML with an ecosystem just large enough to avoid risk F# is a good compromise and will work quite well. TL;DR Nothing's perfect, more similarities than differences, and as long as you aren't blocked language isn't the big factor in project success/failure that many make it out to be.

I broadly agree with your comment but want to correct some misconceptions:

- If we're going to talk about benchmarks games I'd need to see evidence, it's hard to believe .NET can offer anything more performant than say Vert.x or Micronaut :-)

- Scala has type inference (not HM, but then again almost as good)

- Futures/async/await are ubiquitous in Scala, the standard library ships with a very reasonable implementation of Future, and there is no shortage of third-party async libraries/runtimes, even leaving aside Akka which has actually been ported to Akka.NET

- Scala has value types

- No compile-time generics but powerful equivalent capabilities like type classes

- Scala has tail (self-)recusion

- JVM has plenty of low-level performance tricks

- Scala is plenty close to the JVM, I don't know how it is with F# but I know for sure e.g. things like Scala lambdas map exactly 1-1 to JVM lambdas

- Not sure when C# got 'self-packaged bundle' capabilities but if we're talking about that kind of thing, there is GraalVM and you can compile Scala apps to native binaries (as long as they don't use runtime reflection and a couple of other restrictions). E.g. https://cloud.google.com/blog/topics/developers-practitioner...

> you can compile Scala apps to native binaries

I was actually rather disappointed after looking at Scala native. From what I gathered you can’t create libraries (shared or static) only direct executables.

I wasn't referring to Scala Native, I was talking about GraalVM. But either way, compiling to native executables is a huge deal for simple deployment, even without being able to create libraries.

A bit late but I can comment to be informative.

> I'd need to see evidence, it's hard to believe .NET can offer anything more performant than say Vert.x or Micronaut :-)

Benchmarking is interesting because it depends on your case, and the tricks used in the benchmark. Techempower has proxy benchmark at https://www.techempower.com/benchmarks/#section=data-r20&hw=... for just the web layer which shows aspcore quite high. https://benchmarksgame-team.pages.debian.net/benchmarksgame/... I note it isn't as good with DB queries (DB framework) than Vert.x which gives points to Scala for db like APIs but there's tricks there too. Benchmark Game seems to have F# compare well with Java and OcAML (low level non-idiomatic code there I'm sure too). Been meaning to try Vert.x.

- Futures/async/await are ubiquitous in Scala

I need to look into this more but using them in the past it seems very library based (map/foreach/for comprehensions/flatMap/etc) whereas the .NET implementations tend to be like co-routines (state machine) that are compile time constructs with associated perf benefits. It adds a lot to performance; you want the async primitive to be as cheap especially if doing them in hot loops - .NET articles are full of Task/Async patterns and their benchmarks and optimizations to Tasks are constantly ongoing.

> - Scala has value types

Not in a way that I use them I guess. https://docs.scala-lang.org/overviews/core/value-classes.htm... shows quite a few limitations. They may be overcome with Project Valhalla? but it isn't there yet - its a JVM limitation not a Scala one.

They seem to be extremely limiting compared to .NET types where you can compose things together (more than one value, etc). I use this for math ALL the time avoiding any GC events at all - there's a lot of cases for quick stack allocated values that can cross function boundaries. I've worked on the JVM - its possible but much harder with uglier code. .NET seems to have many more ways to avoid "boxing" than the JVM when the JIT doesn't catch it. Especially with generic methods (unless inlined). Basic tuples, ValueTasks (like futures) and such all use this construct at times.

- JVM has plenty low level performance tricks

Sure it does. I'm liking the new things in .NET Core like Span making it "safe" to do them vs Unsafe and interop. Re-using memory (slicing parts of strings without cloning chars), etc in a safe manner is a simple example but there are others.

> Scala is plenty close to the JVM

Plenty of its abstractions however however require allocations and objects (e.g. implicits everywhere) - futures being one. IMO maybe its just me but it is easier to reason about F# performance. An example in F# would be that many allocations are only allowed explicitly (e.g. math conversions) - it actively discourages implicit behaviour for simplicity I feel. e.g. Auto-Boxing in erased generics has caused perf problems in some JVM programs I used to work on. Both platforms are capable and have their own pitfalls of course.

- self-packaged bundle' capabilities

.NET Core feature.

> And at that point I figure why not just use C# - which is a lot more expressive than it used to be

This is what we do. LINQ and switch expressions wherever feasible.

We also integrated a special dialect of SQL into our product which gives us a functional way to specify new views of business state.

Big thing I miss from F# is Option and Either types. You can make both in under 100 LOC in C# but then you have to explain to people how they work or write comprehensive docs.

Also, it feels more natural to me to use dynamic dispatch instead of pattern matching with C#, but I realise the "C# is pure OO" ship sailed a long time ago

While not the same, you can use nullable for that, but you will get some weird look from C# devs.

I make a really big deal about proper use of null in our C# codebase. Getting this right is not hard, just monotonous. Recent language features make enforcing this significantly easier than in the past. Any time something isn't quite right I can expect to find a green squiggle under the offending code, assuming I have declared all of my types accurately.

The only type in our codebase I don't put a ? on would be string. We have very thorough utilization of string.IsNullOrWhiteSpace() throughout, as we prefer the semantics of this for how our application works.

I worked on the team that used the OCaml-based product, but never wrote a line of OCaml (oh well...).

Everyone who worked with the OCaml code was at first bewildered, but then wished they could write everything that way.

The story was that the company bought some code that was written in OCaml, and so OCaml-in-house began.

One of the cameliest devs I know is now writing some Swift, and he says that it has everything an OCaml-lover needs, so you might consider that as well.

As someone that likes F# and seldom uses, the fact it is a third class citizen on .NET tooling makes it hard to justify its adoption in many .NET projects.

If you want to be sure regardless of solutions vendors or framework, there is always tooling available, including Visual Studio plugins, then only C# offers that.

VB comes second after that, due its history on Microsoft tooling, and by being developed alongside in the same Roslyn infrastructure.

C++/CLI gets all the low level goodies, however since it is Windows only and probably due to Midori and Singularity efforts, C# is increasingly getting the same capabilities.

Then at the end comes F#, mostly developed outside of Microsoft, with its own frameworks, doesn't support all .NET features because F# knows better (e.g. protected members) and its advocates don't get enterprise, for example, I don't want to see yet another example of using type providers with World database, show me how to use EF with WPF, Sitecore renderings, SharePoint WebParts, WinUI, ...

The most recent strategy is to try to go after Python and Julia, which I wish them all the luck, but I am not convinced it will work out.

I don't think the risk is that bad. Especially if you are using .NET Core as a cross platform target most of the tooling is made for Windows anyway so is mostly unaccessible. VS Code for example works reasonably as good as any other VS extension - in my experience works better than the Java one. For a functional language it has a pretty good tooling story.

For the standard Docker microservice (e.g. ASP.NET Core, GRPC, etc stack) the tooling is more than adequate.

F# is mostly a superset of C# features these days with a much smaller lag in gaps than the other way around. On the protected members it isn't the biggest issue either - you can still override the method in your app code but it just changes it to public instead for the derived class. For interoperability to override C# classes it works and you wouldn't normally notice any difference; and in F# code you tend not to use the pattern anyway preferring composition to large object hierarchies. So your not blocked from overriding protected members on derived classes if you need to get a method overriden in a 3rd party C# library.

You may be giving the impression that there is missing capability from F# that will cause risk and block your project. Given I've used it professionally I don't think that's actually the case - it won't block you from developing anything that you couldn't do in C#. At worse case - if you actually want to use the really edge case C# features you can always create another project just for that bit of code and call it from F# code given the good interoperability. There's usually ways around this too.

Try to design a WPF/WinUI application in F#, including making use of the designers, Blend and EF.

Some product vendors will decline support if you use F# instead of C# or VB.

You will get told to reproduce the problem in C# or VB, otherwise the ticket gets closed.

Sure - those applications generate C# code in the background so that's that. Its not that F# couldn't do it, but the no language that's coded by a human competes with auto-generated tooling backend (XAML to C# form code). XAML and the widgets behind them are not designed with a code first flow in mind - so when they generate 100's LOC of C# spaghetti I don't expect to code that by hand in any lang. In that case I would choose C#, or more to the point, the Designer and whatever language it generates - the language is secondary.

Those apps you list are also not cross platform as I mentioned in my comment. They are also not the apps you typically code in Ocaml either which is where the comparison/comment was coming from I feel. For cross platform development and some of the software typical when comparing F# to Ocaml, F# IMO is perfectly fine and on par with C# (sometimes better depending on the problem space).

For vendor specific issues F# ports pretty well to C#. For the very odd time you've proven a bug in F# its pretty easy to whip up a quick C# project to give to one of those "vendors" (half an hour/1 hr maybe to build a repro you would need to do anyway). To be honest most of the time they don't help you with C# either - support sadly in my experience is only on paper and never covers you fast enough when you need it. It's just a tick in the box that you have the risk covered should you ever need to resort to this; which many projects never do.

I get your point for some applications, and if you are doing those kinds of apps fair enough. Use the right tool for the job. But I personally think for languages in the JVM/.NET camp the risks of project failure are overblown and aren't as big as people make them out to be especially if they are pragmatic rather than academic and/or syntax heavy. Given they can piggy back on the bigger ecosystem to avoid getting stuck for edge cases.

The issue you will face with using a relatively niche language is development velocity, i.e. the speed at which you can deliver product features.

Niche languages generally have a smaller ecosystem, less documentation, more bugs (due to fewer eyeballs looking at the bugs), and a smaller pool of developer candidates. All of which means projects in those languages will take longer to build than the same project in a mainstream language. If using F# means projects are going to take 2x longer to write because the limited ecosystem means you wind up having to build everything from scratch, or because you have to rewrite a large portion of the code to workaround a quirk in the type-checker that was never fixed, those are project-killing problems.

I'm not saying you can't make it work, but those are the hurdles that you'll have to overcome just to be at the same level as a mainstream language. You have to be absolutely sure that the advantages that F# brings is more than enough to overcome that.

I think we should be careful about throwing around numbers when we have no idea of the actual situation.

For example, if we look at the Total Cost of Ownership of the project, it may turn out that after the ramp of onboarding and initial development has been climbed, it's relatively much faster and cheaper over time to add new features and maintain an existing OCaml or F# project, because its language features highly encourage maintainability and 'clean code'.

It may also be the case that because of type system features like exhaustive pattern matching and abstract types or units of measure, or compile-time code generation, F#/OCaml prevent and save the project from many bugs and incidents which might have plagued the C# project.

Also it may happen that the fact of using OCaml/F# may attract tech talent who may not have otherwise considered your company.

There are many variables and the answer is not really clear-cut.

F# is helped by the easy interoperability with C#: you can call any C# library without problems (you might however want to write a small wraper to reduce friction with F#).

Not any, rather most of them.

F# doesn't support all .NET features.

Try to subclass and override protected members.

I guess that comes down to the real speed impact. A factor of two will kill the idea of using F#. A factor of 1.2 might be okay. It all depends on the pay off. If the bug rate is as reduced as I have seen with my own tinkering then it will be worth rebuilding somethings from scratch.

But I don't know how you justify the risk when there are other mainstream languages that do the job.

Sadly i don't have a link, but i read somewhere about a project written in C# by 5 devs in 7 years. Same project gets rewritten F# by 3 devs without prior experience with it in 2 years. Yes it is easier to rewrite than build from scratch but still pretty impressive IMHO.

Generally speaking the negatives come in as recruitment.

How do you, say, recruit a senior Haskell developer? Or a senior Haskell developer who's good fun to have around the office?

With great difficulty.

I worked at Target on both projects using Haskell and projects using a "standard" tech stack, and we found recruiting Haskell developers a lot easier than other languages. People actively want to work in Haskell!

Using a less common language helps you and your role stand out from the thousands of alternatives top candidates have, showing rather than telling that you're willing to do something different and interesting. Only real caveat is that you need to be willing to either hire people remotely or in larger cities.

If I were to do a startup, I'd use Haskell 50% because I know it really well and 50% because it's a straight-up secret weapon for hiring.

Yeah if I were to list the things that might tempt me to change jobs the number one would be a place to learn a functional programing language in production.

There is only so far you can go with hobby projects and small scripts.

That's actually really cool to hear.

Out of curiosity, of which languages do you think programmers are the most fun to have around? This could be a really fun blog post, or descend into MBTI-style memes.

I'd love to read a study about this. Perhaps tie it into probability lift for venture capital like the other article on the front page today.

I think it's so much about the crowd who gets into those languages.

But there are absolutely Java developers who are about the good times as much as Ruby developers.

They're just outnumbered, but they're there if you look hard enough. I find the good people are there for every language, because the best people are polyglot programmers.

I only refer to F# and Haskell like this because those communities are small.

You can easily meet and greet every single developer who's publicly committed more than 500 lines of Haskell to GitHub in your entire (small) country, by going to a convention.

Not entirely in jest... my favorite "fun language" is actually Forth. It changes your programming style in other languages in mostly good ways.

Ruby developers are usually the nicest and write the best code.

Java developers are dime a dozen.

Typescript developers are pragmatic and will get the job done but have no taste.

Lispers are special.

What a simple minded world view. Do you also believe that “all Americans are …” and “all non-whites are …”?

It's of course tongue in cheek.

I certainly hope so.

I know so

I think Scott Wlaschin has you covered there: https://fsharpforfunandprofit.com/posts/low-risk-ways-to-use...

Yes that is the sort of thing I have been doing. All the little scripts that won't last more than a year or two are in F#. None of these would take longer than a day to re-write in C#.

We tried to write a compiler in a functional language and it went too slow, took too long to write, was hard to find developers. Then half the programers fled, and we had to switch to an adult approach. Now everything is going great. Once in a while a new hires asks whey we didn't write the compiler in a language suited for compiler development, like say Haskell.

Given that many people have found FP languages with algebraic data types excellent for implementing compilers, I am willing to bet it was more of a team issue than a technology issue :-)

> Then half the programers fled

It does sound like something was going on that went beyond the choice of languages.

Ends up being slow for no reason other than a fetishism for FP.

Take a look at LLVM. Its C++.

Every time i got excited about OCaml and every time I tried, I was overwhelmed by syntax, and the fact that there is no type annotations.

I don't understand how that's "a selling point" for the language, it makes the code even less readable than poorly named un-annotated python.

I d really like to use ut though,i think Haskell is the next closest

Not only are there type annotations, but they get their own file—best of both worlds! https://dev.to/yawaramin/ocaml-interface-files-hero-or-menac...

And especially nowadays with the state-of-the-art OCaml plugin for VSCode with type-on-hover and codelens, even reading interface files is super simple.

There are type annotations. The syntax is `(expression : type_expression)`.

Interesting! Is there a more direct link to a particular talk? I'm curious about this, and I see there is a single slide that shows a few bullet points in support of OCaml, but I don't see more than that in this link.

Good question, I haven't been able to find it among the other ICFP video uploads: https://www.youtube.com/channel/UCwG9512Wm7jSS6Iqshz4Dpg/vid...

But perhaps they will upload it later.

"We worked really hard to prove our favorite language has value"

Notice the "Why we like OCaml" page could have been written for C++


Please don't start programming language flamewars on HN. We want curious conversation here.


Oh dear. Looks like the grandparent comment totally ruined this whole post and created another language war that isn't need here.

I really wanted to know how Bloomberg uses OCaml since they are silver sponsors of OCaml [0], but it wasn't clear how they used the language in production anywhere, until right now in this post here.

But unfortunately, the opinions are mostly about X language vs Y rather than the post itself. It is ruined and that's not good at all.

[0] https://ocaml-sf.org/

It would be weird for this slide to list all the things that they don't like.

But that is what they'd have to do with C++, since its problem is not the features it has, but that it has every feature.

> it has every feature.

Not yet, but there is continued enthusiasm for adding more features to C++.

Bjarne wants it to have lifetime tracking. Wouldn't it be nice if your C++ compiler could point out that you can't very well return this object since you've no reason to think it will exist by the time you return?

Vittorio wants it to have Rust-style language version management through epochs.

Herb wants it to offer slimmer exceptions instead of the bulky mess that people often opt to switch off entirely.

All of them agree C++ is too big. However, before it can be slimmed down properly, they just have one more thing to add...

Just like C keeps getting features, C2X is around the corner.

Get ready for lambdas and better generic programming support.

They just keep forgetting to add safer types.

As always, good trolling pjmlp.

There was a proposal of a some lambda thing. From looking briefly over it, it seemed too complicated to me. I hope it doesn't get included.

If you look on the Wikipedia page for C2X, there is no mention of lambda: https://en.wikipedia.org/wiki/C2x .

Instead, pretty much all that is listed there are small and useful cleanups of the standard:

    - two's complemement is required, which I assume will remove some UBs
    - Labels can appear before declarations and at the end of compound statements
    - Removal of K&R function definitions
    - Unnamed parameters in function definitions
In fact those are some of the major complaints people are having with the language currently.

As well as some very useful features that are small and self-contained

    - A feature for binary resource inclusion in the preprocessor (#embed)
    - Binary literals such as 0b10101010
    - _Static_assert
I don't know the process and the people well enough to say I agree with everything WG 14 does, but I'll say that's how you improve quality of life while staying humble. The changes I see here, no chance of having to fix some grand idea over decades, like with RAII which requires N new type specifiers and constructors, move semantics...

Making my best since golden comp.lang.c and comp.lang.c.moderated days.

Wikipedia isn't always up to date, ISO mailings is where the stuff is.



Happy reading.

Following through and skimming your links, I cannot find a lot to back up your claims. The latest discussion of lambda in C that I can find is the following document: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1483.htm

At the very bottom:

> Summary

> The C proposal N1451 provides a new syntax for lambda expressions. While details of the proposal are unclear, it is clear that there are both significant commonalities and significant differences [NOTE: document's title is "Comparing Lambda in C Proposal N1451 and C++ FCD N3092"]. The syntax is in many places a shallow inconsistency. More deeply, the model of reference capture seems inherently incompatible. The N1451 model seems more appropriate to Smalltalk, from whence it originated, than to C/C++. If the C commitee chooses to modify the proposal to be more inline with C++, then syntactic changes to both C and C++ would provide considerable value to working programmers, who often fail to appreciate any differences in approach.

In summary, there seems to be one guy who has invested effort in a proposal, and all I can find is at best mixed reactions to this proposal, inside and outside of WG14.

While the stuff I listed in above comment is all simple or even simplifying the language, and that's what's already integrated in the working draft in the standard.

There are still a couple of years until everything is voted in.

That is how ISO works, one guy writes a paper and then has to champion it.

If you are in an hurry, you can use clang's block extensions, or GCC's version thereof (nested functions).

In any case, C23 isn't K&R C.

Sure. But I think it’s good that there is diversity of languages being used out there. No skin off my back for them to use OCaml so go for it!

> "Why we like OCaml" page could have been written for C++

Okay, so they forgot the "isn't insanely complicated and abtruse" bullet point?

(EDIT: I don't mean to flame on C++. But slides notwithstanding, the difference between the two is pretty obvious.)

Bloomberg is a major ISO C++ contributor as well.

‘Fast compilation speed’? Nah, lol

OCaml compile speeds are not particularly fast.

I'm curious do you have concrete data for this? I would love to see compile times for representative codebases of various sizes across languages and how they've changed across compiler versions.

From anecdotal experience I view OCaml's bytecode compile speeds to be on par with Go's (as a rule of thumb I expect about 1 second per 10k lines). Far slower than something like TCC, but quite fast among other languages.

I do not have concrete data for a comparison of like for like projects being compiled with comparable lines of code, and complexity of the type system usage, running on identical machines.

As for your view of OCaml byte code compilation speed being on par with Go seems rather unlikely or I'm misunderstanding what you mean. Go is not dealing with anywhere as near a complex type system as OCaml and generates a lot more potato machine code (usually not using -mnative so you can know you can copy a binary to arbitrary machines).

Hindley-Milner typechecking in practice is quite fast (as evidenced by ocamlc and Elm's compiler) and easily on par or faster than Go's typechecker (my rule of thumb for Elm code is usually 1 second per 100k lines).

In general HM-based type systems are both conceptually quite simple and can have quite fast implementations. What makes them seem "complex" is that languages with HM-based type systems also tend to make it hard to "go around" the type system and enforce much more rigid discipline around how your code can be written.

One thing that helps OCaml here is that it requires definitions to precede usage (like C/C++). Go and most other languages don't have this requirement. Perhaps, this results in faster symbol table lookups. Moreover, OCaml boxes everything and doesn't produce monomorphized code for generic types. OCaml's code generation is also much simpler than the likes of LLVM or GCC.

> One thing that helps OCaml here is that it requires definitions to precede usage (like C/C++).

I think this is due to the influence of Pascal and/or Modula on the language.

Isn’t pattern-matching alone combinatorial?

What is 'mnt pattern-matching'?

If you're asking about OCaml's pattern matching, it's compiled quite efficiently: https://www.cs.tufts.edu/comp/150FP/archive/luc-maranget/jun...

Apple autocorrects poorly. I meant “isn’t pattern-matching”. And I was asking about checking exhaustiveness at compile time specifically, not about the compiled code.

Unless you generate code with very large pattern matches it's an absolute non issue in practice :-)

There's all sorts of terrifying exponential things that affect HM typechecking in theory (e.g. pattern matching exhaustivity checking as you mention is directly equivalent to SAT, although this is technically not a part of HM typechecking, it is a feature in practice for most HM-inspired languages).

In practice it is actually very rare that they significantly affect compile times because it is very rare for the `n`-size to increase as the size of your codebase increases (these exponential blowups are generally limited to a single expression) for non-pathological code. Basically while you can have exponential blowups inside a single expression, I can't think of a time where you'll have exponential blowups across multiple expressions, which is what counts in a large codebase.

One of the times where big-O analysis doesn't accurately predict real-world runtimes.

I agree that the Ns are typically small, but I don’t know how the implementation could type check as quickly as Go’s considerably simpler type system.

Go's type system is actually of significant implementation complexity (in its details it's actually probably even more complex than Elm although less so than OCaml). In a typical implementation of HM the only nontrivial step (i.e. that isn't just a lookup and then checking if two constants are equal to each other) is union-find, i.e. a couple of set operations, but Go also has set operations it needs to do for type checking interfaces. So there isn't a clear performance win at a high level.

I’m talking about computational complexity. Go might have more edge cases than Ocaml, but I don’t believe it doesn’t have to exhaustively check match statements, for example.

To be clear, I think pattern matching is a good thing.

Indeed I was talking about computational complexity as well there when it came to set operations.

Go and HM-style type systems both have set operations they need to do that are of comparable practical computational complexity.

Exhaustivity checking without pattern matching (e.g. inserting wildcard expressions in every argument to a pattern other than the outermost tag) is computationally very straightforward and is at worst linear in the number of constructors a given type has. This is similar in computational complexity to Go's type checking of interfaces, which is also linear in the number of methods (since Go doesn't have any explicit interfaces it cannot simply do a name lookup).

  // This still checks for exhaustivity and is at worst 
  // linear in the number of `Case0`, `Case1`, etc.
  case thing of {
    Case0 x -> ...
    Case1 y -> ...
    Case2 z -> ...

  // This is how you get equivalent to SAT, but is also 
  // comparatively rare
  case thing of {
    Case0 True False True -> ...
    Case1 True True True -> ...
    Case0 True True False -> ...
    // etc.
Exhaustivity checking in the presence of pattern matching is only non-linear in the number of parameters to the constructor, which in most code is only one (because beyond one usually you would use a record instead). So in practice there aren't any obvious computational edges that Go typechecking has.

The HM type system is designed to be as fast and efficient as possible. And the OCaml typechecker is quite optimized (sometimes to the detriment of readability). Outside of GADTs heavy code, it feels unlikely to me that the exhaustiveness check matters much to the compilation time.

To add some data to the discussion, I have been recently measuring the compilation profile of some OCaml libraries, and the average time spent in the typechecker is around 40% (https://www.polychoron.fr/ocaml/2021/08/19/measuring_compila...).

Thus the complexity of the OCaml type system can only have a limited effect on OCaml compilation time.

And this measure does include interface files, where the compilation pipeline is reduced to just parsing, typechecking, and dumping the computed file to the disk.

OCaml is much faster than Go in the bytecode mode, as you have interfaces files which drastically simplify the typing process.

The compiling pipeline of bytecode is basically typing -> lambda -> bytegen

Bytegen is really close to what Zinc does.

> OCaml is much faster than Go in the bytecode mode

But doesn't the compiled code run slower in bytecode mode? I think that counts.

Sure it does, bytecode is meant for development not for production builds.

The healthy mix of interpreter/JIT/AOT backends is what many languages miss on their toolchains.

OCaml is really good at building in parallel. So even without that many files it can still compile using 32 threads.

Making so that a project with 20k loc is less than a second in debug mode. And recompile time in native(debug) mode is almost 100% linking time.

For bytecode you can have wild rebuild times, in the milisecond range.

Pyre[1] is about 20k loc of ocaml[2].

    $ time make -C source -j8
    make: Entering directory '/home/fnord123/src/download/pyre-check/source'
    ../scripts/setup.sh --configure
    Build info: Linux x86_64 @ Mon Aug 23 2021 (development build)
    abort: no repository found in '/home/fnord123/src/download/pyre-check/source' (.hg not found)!
    Git commit: 1e4dd5a39db1631fd13a3a8df74d6e0f5d801fbc
    dune build @install -j auto --profile dev
           menhir parser/generator.{ml,mli}
    Warning: 31 states have shift/reduce conflicts.
    Warning: 5 states have reduce/reduce conflicts.
    Warning: 77 shift/reduce conflicts were arbitrarily resolved.
    Warning: 75 reduce/reduce conflicts were arbitrarily resolved.
    make: Leaving directory '/home/fnord123/src/download/pyre-check/source'

    real 0m43.700s
    user 4m3.030s
    sys 0m41.005s
I must be doing something wrong.

[1] https://github.com/facebook/pyre-check [2]

    $ tokei
     Language            Files        Lines         Code     Comments       Blanks
     Autoconf                2          235          204            3           28
     C                      15       242641       165361        60669        16611
     C Header               10        14267         2617        11094          556
     Coq                    10         2391         1968          220          203
     CSS                     5          713          540           44          129
     Dockerfile              2           71           42           11           18
     HTML                    2           35           30            0            5
     INI                     1           12            9            0            3
     Java                   31         2654         2080          267          307
     JavaScript              7          867          752           61           54
     JSON                   17          939          939            0            0
     Makefile                5          229          145           45           39
     Markdown               47         7213         7213            0            0
     OCaml                 791       229096       198302        10455        20339
     Python                398        61909        50119         2917         8873
     Shell                   5          111           59           34           18
     SVG                     2            2            2            0            0
     TeX                     4          929          642          129          158
     Plain Text              5          457          457            0            0
     TOML                    1            2            2            0            0
     TypeScript              2          130           84           20           26
     Total                1362       564903       431567        85969        47367

> Pyre[1] is about 20k loc of ocaml[2].

Reading your table, there are 198302 lines of OCaml code. That's about 10x larger than the 20k you claim. The 20339 in that row is the count of blank lines. I doubt they matter much here.

Argh I misread the number of digits.

I'd be curious if the pyre-check compilation is any faster for you either by removing the inlining optimization flag (the `-Oclassic` here https://github.com/facebook/pyre-check/blame/36243764ab81a82...) or whether there's a way to compile pyre-check to bytecode instead of the native compilation step (which unfortunately it looks like there's no easy way to modify pyre-check's build config to do so).

compared to what? it's definitely one of the fastest-compiling languages i use.

Compared to what?

On par with Java but without maven startup time.

Admittedly I’m not citing actual numbers (but then again neither is anyone else here), but subjectively based on past experience, I highly doubt that. The OCaml compile is super parallelizable, with no forward references i.e. potential dependency cycles like in Java. My feeling is it blows Java out of the water.

You don't need Maven to compile Java code, additionally when using Eclipse incremental compiler, files are compiled on save.

I really wouldn't call C++s type system expressive. And neither would I say it's community is small or enthusiastic. And compile times are fast in C++? What are you smoking?

Can OCaml's type system express a type parametrized on a value, eg. like

  template<auto f> struct my_type{};

  using t1 = my_type<123>;
  constexpr t1 val1;
  using t2 = my_type<val1>;


It feels somewhat disingenuous to include a turing-complete meta-programming language (i.e macros) in your definition of the C++ type system.

Because vanilla OCaml doesn't have any mechanism for expressing compile-time computations, the same level of transformations aren't possible, but I wouldn't call this a limitation of the type-system, rather the meta-programming capabilities.

Although, you can somewhat approximate it with modules and functors:

     module type M = sig type t val t: t end

     let my_type (type a) (v: a) : (module M) =
       let module M = struct
         type t = a
         let t = v
       end in
       (module M : M)

     module T1 = (val my_type 123)
     type t1 = T1.t
     let val1 : t1 = T1.t
     module T2 = (val my_type val1)
     type t2 = T2.t

> It feels somewhat disingenuous to include a turing-complete meta-programming language (i.e macros) in your definition of the C++ type system.

But this is literally what a type system is about ? TS definitions don't care about compile-time and run-time ; what you call "macros" are type-level functions which are a byproduct of the expressivity of C++'s TS.

Remember that expressivity of a type system is just the cardinality of the type set you can express with it.

Type systems where you can parametrize on nothing are less expressive than type systems that can parametrize on types, which are less expressive than TS which can parametrize on meta-types, which are less expressive than TS which can parametrize on meta-types and integers (C++98, Haskell afaik), which are less expressive than TS which can parametrize on meta-types and arbitrary compile-time values (C++20, soon Rust I believe ?), which I'd guess are less expressive than full-blown dependent types but I'd need to check the exact definition

Actually you can extend OCaml with similar mechanisms via ppx, apparently op isn't aware of it.


TIL about it, last time I wrote some OCaml camlp4 was still all the rage

Yeah, it was decided to merge efforts and integrate into the compiler around the time the Real World OCaml book came out.

> TS which can parametrize on meta-types and integers (C++98, Haskell afaik)

Haskell also supports type-level strings and type errors, as well as arbitrary ADTs if defined with the -XDataKinds extension enabled. Not sure what you mean by ‘parametrizing on meta-types’ though.

Where does that definition of expressivity come from? As far as I can tell the cardinality of the ‘type set’ is omega_0 in both C++ and OCaml.

I haven't used ocaml but I think something is possible with GADTs. Beside the example you have given shows C++ templating prowess and not its type system. In general I wouldn't call a type system that doesn't support ADTs expressive.

But GADTs are expressible in the C++ type system (thanks to templates which are just the C++ implementation of the parametrizable types typesystem-level property)

No, but on the other hand it doesn’t monomorphize generics and make the compiles dog slow and the binaries bloated :-)

Well, but it does make the code slower

I knew someone would say that :-) ‘Slower’ is super subjective. What is the actual performance requirement? And remember that the OP thread is talking about an extremely high-performance RPC system. Sure, it may not be as fast as C++, but again, what is the actual performance requirement. You don’t go around just throwing ‘Eh, not as fast as C++’ at everything you come across :-)

How is slower subjective? Performance of two pieces of code can be objectively measured. There is nothing subjective about it.

Who cares? What use is measuring the objective performance difference between two solutions if no one cares about the difference? That’s why I’m talking about the actual performance requirement.

But what when people care ? I work in a field where individuals regularly spend 4 to 5 figures for a new CPU for a half-percent of single-core performance improvement ; computers will literally never be fast enough for what we want to do with them so our job is to leave exactly zero possibility for improvement through software

If you care about it, then obviously go to town on optimization, use an FPGA, whatever. But I notice you said 'CPU', which means there is a performance/cost tradeoff even in your field. Otherwise wouldn't you be using specialized hardware?

When we are talking about a general-purpose language like OCaml, then you come in talking about extreme HPC–you must realize it's not relevant in the discussion? Would you comment on threads about Golang talking about how it's not appropriate for HPC usecases?

I mean, yes, FPGAs and dedicated hardware are researched and used in that field. The main reason CPUs are used "more" is due rather to convenience, and what people want to do not easily being doable on an FPGA ; for instance one of the often used programming languages there (Faust) just got an experimental FPGA port after 20+ years of existence.

But when talking about performance the point is to extract the absolute most performance possible of a given hardware, because you are building a product and you can only afford a.g. some ARM chip and need to get the most out of it to make your product's price fit for its target demographic (to, you know, make money for your business).

In particular, the main "competition" in that field is analog hardware which does not have "performance" issues (but others instead : an analog guitar distortion does not have latency but it does raise the noise floor in the signal) - everyone wants the best of both worlds and it is our job to make it happen

That's an incredible arrogant and close minded way to think about software. There are plenty of domains where every extra % of performance matters. Video games, HFT, robotics etc.

Even in these domains people still balance performance against the expressiveness of the language, or else why wouldn't everyone write their games/HFT/robotics in assembly for that "bare metal" performance?

For example, many popular game engines provide C# scripting engines _even though_ using a garbage collected language (even just for scripting) is slow (and throwing away % of performance) because the performance is _good enough_ for their usecases.

That's because scripts in a game are not usually a performance bottleneck if kept to best practices. And you can bet your ass both unity and unreal have some ASM in their guts.

Yes, my point exactly, and in the places where people use OCaml, the overhead from not monomorphising generics isn't a performance bottleneck.

That's a self fulfilling prophecy. Of course OCaml isn't used there... Because it can't hit the performance target.

Exactly, I mainly work on a C++ software and for my PhD rewrote all the core algorithms in OCaml... Boy that was slow, I'm never ever touching that language again for anything that needs peak performance.

Yeah, that's why I asked, who cares? If you care about it, then obviously you need it. Also, https://news.ycombinator.com/item?id=28283376

Well, Jane Street do HFT in OCaml.

This is a pretty obnoxious way to tell me you don't work on real-time systems without explicitly saying you don't work on real-time systems.

I didn't realize we were exclusively talking about real-time systems? Do you think people you argue with on HN are so dumb that they would go around claiming high-level GC languages are suitable for real-time systems?

I feel like if your concern is real-time systems, you should probably say you don’t think it’s suitable for real-time systems, instead of talking about performance differences that no one really disagrees but which don’t matter in practice for most applications anyways.

Asking about the actual requirement sounds very much like what you do in real-time systems, vs just being happy with "faster"?

Please do not argue like that. I understand you are a fan of the language, I love the OCaml language myself. But pretending execution speed does not matter is disserving the cause, especially since many people came to OCaml because it's a great trade off between execution speed, expressivity and safety.

Please re-read my comment :-) Nowhere did I say:

> execution speed does not matter

What I actually said: what is the actual performance requirement?

I don’t get why this is such a hard concept for people in this thread :-) Obviously you should use the right tool for the job! If you have extreme HPC requirements, no one (except maybe Jane Street) is going to use OCaml for that. It’s a high-level GC language! When you come into a thread, have some basic context about what it’s about.

"Slow" can be subjective indeed, but not "slower".

Not exactly types, but with functors you can parameterize modules on values. As far as I can tell it ends up being very similar.

No, the type system parameterizes on types.

People are downvoting but the GP's point does not really have anything to do with type systems. I think this example is closer to macro expansion than a typesystem.

How is paramerization of types on values not a typesystem-level property ? There's definitely nothing akin to macro expansion involved since you can do type-level computations that way

I’m pretty sure the implication is that since template instances are made specific at compile time and are done so in a way that is very analogous to AST based macro expansion. Specifically, C++ templates are a programming language which runs during the compilation of C++ code that acts by producing valid C++ AST components.

I have never seen a definition of type system which cares about things being done at compile-time or runtime, or that there even is a compile-time or run-time

Never heard of static vs dynamic type checks ?

Expressiveness of type systems is about which types (and thus type checks) you can express, not when the type checking occurs

I am guessing it could using PPX


In C++ the system for generics is the same system for metaprogramming. In OCaml they are two different systems.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact