[HN Gopher] We chose OCaml to write Stategraph
___________________________________________________________________
We chose OCaml to write Stategraph
Author : lawnchair
Score : 140 points
Date : 2025-11-07 13:10 UTC (9 hours ago)
(HTM) web link (stategraph.dev)
(TXT) w3m dump (stategraph.dev)
| cyberpunk wrote:
| > Most systems handle this defensively with locks and runtime
| validation.
|
| So i work at an org with 1000s of terraform repos, we use the
| enterprise version which locks workspaces during runs etc.
|
| everywhere else i've worked, we either just use some lock
| mechanism or only do applies from a specific branch and CI
| enforces they run one at a time.
|
| My question is: who is this aimed at and what problem is it
| actually solving? Running terraform isn't difficult - thousands
| of orgs handle it no problem - the issues I have with it with it
| have never been around lock contention and race conditions..
| ab5tract wrote:
| Agreed and well said!
| sausagefeet wrote:
| Hello, CTO of Terrateam here, the creators of Stategraph.
|
| As you said, the common practice is to use locks on state to
| guarantee that operations don't step on each other. This works,
| however the cost is that if it takes 5 minutes to perform an
| operation, only one person can be doing an operation at a time,
| so if 5 devs are modifying infrastructure, the last one has to
| wait 25 minutes just to get back the plan, even if those 5
| people are not changing overlapping resources in the state.
|
| The way that most people deal with this is they take their
| infrastructure and break it up across multiple root modules,
| and then when those root modules, break it up again, etc.
|
| Stategraph is solving the problem of getting all of the
| performance benefits of breaking up your root modules without
| breaking up your root modules. It dynamically determines which
| resources each of those 5 devs are operation on and, if the
| resources do not overlap, can run them in parallel.
|
| That means Stategraph is manipulating state in a bit more
| sophisticated way than standard Terraform/Tofu, and we need to
| be careful we don't get it wrong.
| pjd7 wrote:
| I'm not sure I would want this even if I could have it TBH.
| Engingeering org size is about ~200 with infra/sre/ops around
| ~25.
|
| Different teams want to move at difference cadences. At a
| certain scale splitting up things feels a little more natural
| (maybe I am stockholmed by prior limitations with TF though
| or just used to this way of operating now).
|
| But even then, we're moving to k8s operators to orchestrate a
| bunch of things and moving off terraform apart from the stuff
| that doesn't change much (which will eventually get retired
| as well). Something like https://www.youtube.com/watch?v=q_-
| wnp9wRX0
|
| Terraform variable management is our larger problem
| (now/nearterm) when we have to deploy numerous cells of infra
| that use the same project/TF files with different variables.
| Given the number of projects/layers of TF getting cell
| specific variables injected is meh.
|
| Those variables are instance size, volume size, addresses,
| IAM policy, keys etc.
|
| This is in the b2b saas world with over a million MAU. We've
| got islands of infra for data soverignty, some global cells
| where each cell can communicate back / host some shared
| services (internal data analytics, orchestration tooling,
| internal management tooling and the like).
| leoqa wrote:
| Yeah I get the sense that terraform change application is
| solved by just serializing all changes? The concurrent applies
| isn't that big of a deal?
| sausagefeet wrote:
| > The concurrent applies isn't that big of a deal?
|
| That depends. There are many organizations (we talk to them)
| which have plans and applies that take 5 - 10s of minutes,
| some even close to an hour. That's a problem. We talked to
| one customer that a dev can make a change in the morning and
| depending on the week might have to wait until the next day
| to get their plan, and then another day to apply it, assuming
| there are no issues.
|
| If you're in that position you have two options:
|
| 1. Just accept it and wait. 2. Refactor your root module to
| independent root modules.
|
| (2) is what a lot of people do, but it's not cheap, that's a
| whole project. It's also a workflow change.
|
| Stategraph is trying to offer a third option: if your changes
| don't overlap, each dev can run independently with no
| contention.
|
| Even if one doesn't think contention over state is a big
| deal, I hope that one can agree that a solution that just
| removes that contention at very little cost is worth
| considering.
| yawaramin wrote:
| > There are many organizations (we talk to them) which have
| plans and applies that take 5 - 10s of minutes, some even
| close to an hour. That's a problem. We talked to one
| customer that a dev can make a change in the morning and
| depending on the week might have to wait until the next day
| to get their plan, and then another day to apply it
|
| That's us. Especially because our teams are distributed
| across NA/Eastern Europe/Japan. So getting a lock is a
| problem because you have to wait for someone else to
| finish, then getting the required reviews is a problem
| because you have to wait for people from other timezones to
| come on, then by the time you're ready to re-plan after the
| reviews someone else has taken the lock, then you have to
| wait for them,...
| cyberpunk wrote:
| If there was a time to insert a Jobs "you're holding it
| wrong" I think it would be here...
| yawaramin wrote:
| They were, in fact, not holding it wrong
| internet_points wrote:
| Back in the day, before git, we had RCS. Developers would just
| lock files when they worked on them, and then unlock when they
| were done. Or you'd copy a folder manually ("branch") to work
| on things concurrently and then punch them in the shoulder when
| they forgot to unlock master so that you could lock it and
| check in. It worked absolutely fine, there were loads of
| workarounds!
| koakuma-chan wrote:
| Is any of this OCaml specific? You can check all boxes with
| TypeScript.
| stefanos82 wrote:
| Does TypeScript emit machine code? OCaml gives you this option,
| if you need it.
| procaryote wrote:
| But they say "we use ocaml [because it has types]" not
| "[because it can emit machine code]"
| koakuma-chan wrote:
| I would go for Rust if I wanted machine code
| a-french-anon wrote:
| Well, TS transpiles to JS which then runs on Node, aka V8, a
| native JIT compiler. So yes, I guess?
| pjmlp wrote:
| Kind of, given that V8 performance is never going to be as
| good as AOT compiled language, and JIT needs warmup time.
|
| It is no accident that famous JavaScript tools keep being
| rewritten into C++, Dart, Go and Rust.
| zaphar wrote:
| Those two type systems are _not_ the same. Typescript has some
| soundness issues in the type system. They are there because
| they have to work seamless with javascript so it 's
| understandable. And they improve many codebases that would have
| been otherwise written in javascript. But they do not in any
| way give you the same level of guarantees that OCaml, Haskell,
| or Rust would give you.
| koakuma-chan wrote:
| For all practical purposes I believe it does the same thing.
| zaphar wrote:
| If you are choosing ocaml they most definitely do not do
| the same thing for all practical purposes.
| phplovesong wrote:
| No...no you cant.
| yawaramin wrote:
| It's explained in the OP.
| laszlojamf wrote:
| I don't really get what's special about OCaml with these points
| they raise? Wouldn't almost any strongly typed language do?
| Wouldn't TypeScript also tick all these boxes?
|
| EDIT: I wouldn't choose TypeScript either for this type of use
| case, but not for the reasons they state, that's my point
| jacquesm wrote:
| It's the combination with concurrency that makes this a hard
| problem.
|
| And OCaml excels at solving that sort of problem. OCaml and
| Erlang are the only two languages that I'm aware of that have a
| really clean way of doing this, in most other languages there
| is always some kind of kludge or hack to make it work and at
| best you're going to do something probabilistic: it seems to
| work, even under load, so it _probably_ is good now. Until six
| weeks later on an idle Tuesday the system deadlocks and you
| have no idea how it happened.
| internet_points wrote:
| What advantage does OCaml have over Haskell here? I find
| software transactional memory in Haskell so simple to work
| with that I have lost all fear of concurrency, but what am I
| missing out on?
| phplovesong wrote:
| Its mostly about practicality. Haskell is kind of pain when
| you need IO, as in when you go there there is no way out.
|
| Ocaml is more practical, and less punishing (you can do IO
| without monads), but the most important diffrence is
| performance. Haskell is VERY hard to make predictable
| because its lazy. Ocaml is strict so general system
| performance is much easier to predict.
|
| But they are sibling languages in my book, while i still
| prefer ocaml over haskell.
| myaccountonhn wrote:
| Also IMO the dev tooling is better for OCaml. Far better
| compile times.
|
| A big part of interacting with APIs (which I imagine
| Stategraph does) is just dealing with records, and
| working with records in Haskell is really annoying unless
| you bring in lenses which bring a lot of complexity.
| phplovesong wrote:
| Tooling was kind of bad previously. But dune has got
| really good. I like the new dune developer preview thing
| that is still in beta.
|
| https://preview.dune.build/
| yodsanklai wrote:
| I personally find OCaml more pragmatic than Haskell.
|
| Haskell has a steeper learning curve IMHO: monads are
| pervasive and are hard to understand, laziness isn't a
| common programming pattern and it adds complexity. I find
| type classes confusing as well, it's not always clear where
| things are defined.
|
| I like that OCaml is close to the hardware, there are no
| complex abstractions. The module system makes it easy to
| program in the large (I love mli). If you avoid the more
| advanced features, it's a super simple language.
| gregwebs wrote:
| Functional programming is immutable by default. TypeScript and
| many other typed languages don't really stop you from
| clobbering things, particularly with concurrency. Rust does.
| But immutability with GC is a lot easier to use than Rust if
| you don't need the performance of Rust.
| galangalalgol wrote:
| But what is functional besides haskell? Purescript? Elm I
| guess. Ocaml is not. It has for loops even. You can write
| pure functional ocaml but people don't. It mattered a lot
| less when it didn't have true concurrency, but now clobbering
| things in ocaml is quite possible.
| greener_grass wrote:
| Define functional?
|
| Even Haskell is not functional in the strictest sense. It
| has unsafe IO. It can throw exceptions. Functions may not
| halt.
| galangalalgol wrote:
| Fair. Agda, gallina, f* maybe?
|
| My point was that without any escape hatches or magic you
| can code a segfault starting in ocaml5. That may be true
| of haskell? It is true of rust too, though the only known
| way to do it isn't something that is likely to happen by
| accident and is tracked as a bug. In ocaml5 if you use
| domain, it is down to experience skill, and some luck to
| be sure you used atomic when necessary. I'm a bad
| programmer despite going on four decades of experience.
| I'm not even remotely methodical. If I adopt ocaml for a
| project I'm using 4 or adding something that fails the
| pipeline if it finds domain anywhere.
| greener_grass wrote:
| Shouldn't most application programmers in OCaml be
| reaching for EIO or some other well-tested abstraction?
| galangalalgol wrote:
| When there was no true concurrency, only preemption, eio
| was safe. Not now, you can still clobber things.
|
| Edit: i was wrong! Since at least 5.1 it catches the
| cross domain access and errors gracefully.
| debugnik wrote:
| Eio didn't exist before multicore OCaml actually, it was
| designed for it.
| debugnik wrote:
| > you can code a segfault starting in ocaml5
|
| It shouldn't, the OCaml 5 memory model bounds the reach
| of data races in both space and time. [1] Thread-unsafe
| code won't be correct when misused, but it will stay
| memory safe unless you reach for an additional escape
| hatch (or you find an implementation bug of course).
|
| [1]: https://ocaml.org/manual/5.4/memorymodel.html
|
| I'm much more concerned about the amount of poorly vetted
| escape hatches in wide use in OCaml, mainly for bindings.
| galangalalgol wrote:
| You are right! As of 5.1.1 at least it catches the cross
| domain access I was using to smash things. From what I am
| reading it sounds like it didn't work in 5.1 I could go
| try it in godbolt to find out when it was fixed, but I
| kind of don't care. Very exciting, I like ocaml and was
| lamenting the changes.
| debugnik wrote:
| That makes a lot more sense: The earliest 5.x releases
| weren't stable at all despite the non-prerelease version
| numbers. I waited for longer than I wanted to before
| upgrading from the LTS to 5, but right now it should be
| ok to switch as long as the few regressions, like the GC
| pacing issue, don't affect you workload.
| e12e wrote:
| StandardML (standard metalanguage), scheme?
| galangalalgol wrote:
| Doesn't scheme have a set macro? I think for purity you'd
| have to go to something used for proofs that doesn't
| actually interact with the world.
| phplovesong wrote:
| FP has nothing to do with mutability. You seem to lack a
| basic understanding what the common FP languages are, and
| what FP actully is.
| galangalalgol wrote:
| Pure functional programming languages do not allow
| mutable state. They can simulate it with monads, and they
| typically have impurities to allow io sode effects, but a
| for loop is an inherently imperative construct. Mostly
| functional languages like ocaml have things like ref.
| Functional languages aren't just about functions being
| composable and having no side effects, they also require
| that once a value is bound to a name, that it does not
| change. That isn't just pedantry either, it is what
| allows safe concurrency by default.
| phplovesong wrote:
| Ocaml is immutable. It has ref if you need mutation, this
| is not the default thing you grab tho. Haskell has unsafe
| and IORef, that does basically the same thing. Scala,
| rust etc has all escape hatches.
|
| A loop by itself is not non-fp, as i can do the exact
| same thing via recursion. Its just syntax.
|
| Hell, i can write a never halting program in lambda
| calculus with a fixed point combinator causing "undefined
| behaviour".
| pjmlp wrote:
| That is only true since people started equating FP with
| Haskell.
|
| OCaml as the discussion subject on this thread, allows for
| mutable data structures, and I am old enough to have been
| taught Lisp as one possible avenue for FP.
| greener_grass wrote:
| * Stategraph manages Terraform state, so correctness isn't
| optional
|
| TypeScript has soundness issues that OCaml does not have
|
| * Strongly-typed data structures catch field errors at compile
| time
|
| TypeScript does have this, although the guarantees are in
| practice weaker since libraries may have incorrect type
| definitions
|
| * Type-safe SQL queries prevent schema drift before deployment
|
| There are TypeScript libraries that offer this, so fair point!
|
| * Immutability by default eliminates race conditions
|
| TypeScript is not immutable by default
|
| * PPX generates correct JSON serialization automatically
|
| TypeScript does not have an equivalent to PPX infrastructure
| AFAIK. If there is, it's definitely not as widely used within
| the ecosystem compared to PPX for OCaml.
|
| Edit: Downvoters care to respond?
| IshKebab wrote:
| OCaml has a much stronger type system than Typescript.
|
| The real question is "why not Rust?". I've used both a fair bit
| and OCaml's only major advantage IMO is compile time. That
| doesn't seem compelling enough to put up with the downsides to
| me.
| toolslive wrote:
| Ocaml has a garbage collector. It's less of a struggle than
| Rust.
| jitl wrote:
| Rust is a pain
| sausagefeet wrote:
| I'm the CTO of Terrateam. For "why not rust", I have found
| the downsides of Rust not compelling enough to use it. We
| don't need close-to-metal performance. We don't really need
| the borrow checker, a GC is fine. We are immutable by default
| so the borrow checker doesn't help much there.
| pjmlp wrote:
| Great choice by way, I feel too many reach out for Rust,
| because they lack the perspective ML type systems are not
| something introduced by Rust, rather a long linage of
| languages since ML/Standard ML.
| sausagefeet wrote:
| The sense I get from some comments is that if you need a
| type system and to compile to an executable, Rust is the
| only option, otherwise you can use pretty much anything.
| But, as you say, MLs have been compiling to binaries for
| decades. One of the first online books on OCaml is for
| systems programming.
|
| I know that isn't everyone's view, but I do hope posts
| like this, even if not technicaly deep, at least let
| people know that there are lots of options out there.
| schonfinkel wrote:
| Plus, OCaml kept the SML tradition in its robust module
| system, with modules as first-class citizens and ML-style
| functors, this is something hard to see nowadays, even
| among ML-inspired languages.
| pjmlp wrote:
| Answer is easy, not everyone needs the performance boost
| provided by borrow checker, 99% of the time some kind of
| automatic resource management is good enough.
| linhns wrote:
| I don't like OCaml myself, but I'd pick it over rust here as
| bare metal perf is not necessary, and time wasting fighting
| the borrow checker is just not worth it.
| IshKebab wrote:
| It is worth it in my opinion because it's mostly a one-time
| cost (learning how it works and what is allowed), and in
| return you get less buggy program structures (it basically
| forces you not to write spaghetti code). Also you have to
| worry about it _much_ less if you don 't need 100%
| performance and are happy to clone everything.
|
| Occasionally I do still fight it, e.g. if you want a self-
| borrowing structure there still isn't a great solution (I
| think Rust should support position independent borrows) but
| overall it's fine.
| phplovesong wrote:
| Rust has too many footguns. Ocaml is usually more safe.
| sausagefeet wrote:
| Certainly there are specifics between the type systems that
| differentiate. TypeScript generally chooses to enforce types in
| an ergonomic way whereas OCaml chooses to enforce types in a
| sound way. Whether or not that is a meaningful differentiator
| for someone is up to them.
|
| This blog post shows the elements of OCaml that motivate us to
| use it. Is it complete? No. Maybe it should be more explicit
| that we like using OCaml, and these technical aspects aren't
| unique but certainly benefits we see.
| pjmlp wrote:
| Besides the better type system, OCaml is a compiled language,
| you don't need workarounds like rewriting code in Rust and Go,
| as it happens on TypeScript/JavaScript world.
| phplovesong wrote:
| Typescript (javascript) is kind of a joke compared to what
| ocaml brings to the table.
|
| Ocaml has a top in class typesystem, a "faster than Go"
| compiler and (in 2025) good tooling. It allows you to say fuck
| it and write a while loop if you need to. Hell you can even do
| OOP. Also it has an incredible module system and full type
| inference. It also has an effect system, and good concurrency
| features (ocaml 5).
|
| I cant say many other languages that has all the same features.
| hardwaregeek wrote:
| I like OCaml and have written the "why we chose XYZ language"
| posts. Most of the time the real answer is "we like it and it
| makes us feel good to use it". Like the answers aren't _wrong_
| per se but they 're more post-facto justifications. And that's
| perfectly fine! I think we should normalize saying that tech
| stack choices are subjective and preference-based. We're not
| robots. The social and aesthetic parts of a stack matter to
| people
| criddell wrote:
| The first comments here are people reading this as if the
| authors are saying _only_ OCaml can do this. They aren 't.
| sausagefeet wrote:
| Exactly! OCaml is the language I like to solve problems in,
| and I'm excited to solve problems in, so that's why Terrateam
| uses OCaml (I'm the CTO). You can do a lot (but not all) of
| this in Go, or TypeScript, but I don't get excited about
| those languages. Certainly I'll use them if I have to (our UI
| is written in Svelte) but building your own company is a
| grind, and using OCaml makes the grind just a bit more
| exciting, and that's an edge.
| xedrac wrote:
| I've used a lot of Rust and Haskell over the past few years
| (I consider OCaml to be similar), and I think the benefits
| go beyond just user preference. But I think it's something
| that requires experience with "must not fail" systems
| failing in production, and then seeing how these languages
| make that failure impossible. The level of freedom and
| confidence that brings is amazing. And yes, that also makes
| them more fun to use.
| mattgreenrocks wrote:
| Industry is just starting to come around to it, but I've
| never been more happy programming than when using a
| strongly-typed language with sum types. What most people
| fail to understand is that fighting the type checker is
| almost always a feature, not a bug. It is training you to
| write code in a way that it understands, which forces your
| thinking to be less sloppy, even on non-happy paths.
|
| Sum types enable much higher levels of expressivity of what
| valid states are while still being statically analyzable.
| Any new PL lacking them, IMO, is making a huge unforced
| error. They don't apply for every situation, but they do
| handle a large amount of day-to-day programming concerns.
|
| Side note: OO is oft-maligned in OCaml, but I really
| appreciate that they included it anyway. I much prefer
| languages that give you a set of tools to use in whatever
| situation you find yourself in.
| lawnchair wrote:
| Definitely agree. Most teams don't choose a language for purely
| rational reasons, and we're not pretending we did.
|
| We like OCaml, it makes us excited to build. We know the
| language deeply, which means we can reason about performance
| and behavior before we run the code. We can onboard new
| engineers quickly because the type system forces clarity.
|
| The runtime is simple enough that we can predict what it's
| doing. So yes, part of it is that OCaml feels good to use. But
| that feeling comes from years of watching it make complex
| systems simpler to reason about, not harder.
| whobre wrote:
| Using something you enjoy is fine, as long as you don't forget
| the person who is going to maintain your code after you move
| on.
| strongly-typed wrote:
| Imagine inheriting a project that was a joy for someone to
| work on instead of a slog.
| blandflakes wrote:
| Joy is probably an improvement on slog, but one person's
| joy is another's hell. I've inherited a lot of joyful
| projects that were an awful fit for continued maintenance
| because joyful meant using new, unproven, and unstable
| technologies.
| whalesalad wrote:
| I have experienced many identical situations. Some people
| just love to tinker and over-engineer for the sake of
| stretching a muscle. It's unfortunate when that sneaks
| into a prod system that has to be maintained by others.
| lenkite wrote:
| Parent developer's Joy is the inheriting developer's
| Despair. A project is successful when the parent developer
| is Bored.
| BenGosub wrote:
| I think that in some cases where OCaml was chosen, like Docker
| or some parsers, the choice was obviously not evangelical. But
| I agree that in most cases it is a post-facto justification.
| yahoozoo wrote:
| Docker?
| lo_zamoyski wrote:
| > I think we should normalize saying that tech stack choices
| are subjective and preference-based. We're not robots. The
| social and aesthetic parts of a stack matter to people
|
| I would just like to distinguish "subjective and preference-
| based" from "social and aesthetic" and also clarify some
| notions.
|
| 1. The social _is_ objective. We are social animals. It is
| essential to what it means to be human. We need social
| relations to grow and develop and to become more human.
|
| 2. The aesthetic _is_ objective. We confuse taste with beauty,
| and this is perhaps the legacy of influence of certain
| philosophical traditions on our thinking. Beauty has to do with
| the fullness with which some thing instantiates a form and
| realizes some end /good. So, when it comes to artifacts like
| programming languages, a beautiful language will satisfy some
| _human_ purpose more perfectly than a less beautiful language.
| Taste is a matter of subjective disposition to beauty. Someone
| with bad or poor taste might prefer the inferior over the
| superior, for example, or fail to discern between the two.
|
| We sort of create mystery about preference here, as if they
| were just arbitrary, immutable, inexplicable brute facts. But
| preferences can be more good or less good or even bad. Note the
| relation between preference and taste.
|
| > We're not robots.
|
| 3. Typically - and I do not accuse you of this - this is meant
| to mean that what makes us human compared to robots is that we
| have emotions. But it isn't that. Many animals have emotions.
| What makes us distinct as human beings is the intellectual and
| the rational, which robots (as computational instruments) are
| not.
|
| 4. Post hoc rationalizations may not stand behind the actual
| motivations, but the content of the rationalization may remain
| true and valid nonetheless.
| ab5tract wrote:
| I'm sorry but there is no way you can demonstrate a universal
| aesthetic. Your opinion of other people's tastes does not
| reflect on their taste -- it reflects on yours.
| lo_zamoyski wrote:
| > I'm sorry but there is no way you can demonstrate a
| universal aesthetic.
|
| What do you mean by "aesthetic", because I've already made
| the distinction between objective beauty and subjective
| taste. If my explanation is true, then it follows that
| there is an objective ordering of beauty (of at least two
| kinds: with respect to the same form/end, and between forms
| and ends). Then, there's the question of how competent
| someone is at recognizing this order. And finally, there
| are contingent factors that will affect expressed
| volitional preference as a function of factors like
| attainability or character flaws or whatever.
|
| Making beauty a matter of purely subjective response makes
| it more mysterious and nonsensical, not less.
|
| > Your opinion of other people's tastes does not reflect on
| their taste -- it reflects on yours.
|
| How do you know this? _You_ haven 't demonstrated this
| claim. I've at least explained the basis for mine.
|
| I claim that on the contrary, yes I can. I can claim that
| someone who thinks rape or murder are beautiful has
| objectively deranged tastes, because these acts are
| intrinsically ugly.
| svara wrote:
| One of the great understated reasons for using 'cool'
| programming languages is that it allows a business to hire
| selectively for people for whom programming is a passion.
| epolanski wrote:
| > I think we should normalize saying that tech stack choices
| are subjective and preference-based.
|
| True but I don't think this is the correct way to frame it,
| because it sounds unprofessional.
|
| The correct, and understandable way is:
|
| - the language has properties that fit the software
|
| - the language has properties that fit the development process
|
| - it boosts morale of development team
|
| - the team has the required skillset
|
| Then it's not only a preference, but a conscious engineering
| choice made of evaluating the different pros and cons of
| various alternatives.
| keyle wrote:
| There are many languages that fit these requirements. I don't get
| the purpose of writing these posts besides a reason to go viral
| and get clicks talking about your product.
|
| I write OCaml myself, but not for $paid job, it's okay, it's a
| fine language although with cruft, but it's not the panacea
| described here.
| celpgoescheeew wrote:
| I hope for the team to settle with a FLOSS license, so it becomes
| feasible to evaluate for everyone.
| lawnchair wrote:
| We're still figuring out what balance makes sense between
| openness and sustainability, and we'd rather take the time to
| get it right than rush into a license we'll regret later. The
| goal is for Stategraph to last a long time.
| yawaramin wrote:
| Best of luck. Hoping for AGPL :-)
| iLoveOncall wrote:
| Everyone makes mistakes, it's good to admit them.
| sgarland wrote:
| TIL (via a rabbit hole after reading this) that a good type
| system removes an absurd amount of boilerplate validation code.
| yanis_t wrote:
| This is pretty much obvious for people migrated from JavaScript
| to TypeScript and suddenly realised that most of their unit
| tests can now go to a trash bin.
| sgarland wrote:
| I'm speaking from a Python background. I love types and use
| them religiously, but I had no idea how much better (modulo
| runtime checks; that one's obvious) others were at it.
| Hasnep wrote:
| Do you have any good resources on this subject? I agree and
| would like to see what a persuasive argument for it looks like.
| ebonnafoux wrote:
| The very classic parse don't valide if you haven't already
| read it : https://lexi-
| lambda.github.io/blog/2019/11/05/parse-don-t-va...
| strongly-typed wrote:
| Here's my take. It helps you enforce properties about your
| data. Didn't mean to make this response so long, but alas.
| (* Quick note on notation: I will use "double
| quotes" when referring to _values_ and `backticks` when
| referring to _types_. *) (* Think
| of this as an interface. It defines the shape of a module.
| Notice that the interface describes a module that defines a
| type called `t`, and two values: "of_string", and
| "to_string", and they are functions with types: `string ->
| t`, and `t -> string`. *) module type ID = sig
| type t val of_string : string -> t val
| to_string : t -> string end (*
| Below this comment is a module named "Id" that _is of type_
| (in other words: it _implements the interface called_) `ID`.
| Due to the explicit type annotation (Id : ID), now from the
| perspective of anywhere else in the code, the exported
| interface of the module "Id" is `ID`. Modules
| only contain two things: `type declarations`, and "values".
| Values are your primitives such as 1, '<', "hello", but also
| composite such as (fun x -> x + 1), (Some x), f x, { foo =
| "bar"; baz = 42 }, and even (module Id) (yes! modules can be
| values too!). Type declarations tell the compiler . Anything
| which is a value _always_ has a type that can _usually_ be
| inferred. No type annotation is necessary when
| the compiler correctly deduces the type of your value through
| static analysis. For instance, in the module below,
| "of_string" is deduced to be of type ('a -> 'a). The ' on the
| symbol 'a signifies a "type variable", and it means that it
| can be filled in with any type. For instance (t -> t) and
| (string -> string), but not (t -> string) or (string -> t).
| For those it would have to be of type ('a -> 'b). We cannot
| deduce this type, however, because our implementations do
| nothing with their inputs besides return them. Since nothing
| is changed, it's always the same type. Now, can
| you spot the pink elephant? Notice how the "ID" interface
| from above defines "of_string" to be of type (string -> t).
| How can this be possible? It's because we gave the compiler a
| hint when we said `type t = string`. This says that a "value"
| of type `t` is backed by a value of type `string`. If
| something type checks as `t`, it also type checks as
| `string`. So, we could reason through and say
| ('a -> 'a) can be instantiated to (t -> t), but `t` is also
| equal to `string`, so we can mentally imagine a hypothetical
| intermediate type... something like ({t,string} ->
| {t,string}). This type and type equality is visible _inside_
| the module. But when the `ID` interface was applied over the
| `Id` module as in (Id : ID), this has the effect of hiding
| the type equality (the fact that `type t = string`) because
| in the `ID` interface we define `t` without an equals sign:
| `type t`. This forces us to _choose_ a concrete type to
| expose externally, even though the type is less general than
| what the implementation sees. NOTE: OCaml
| doesn't use parens for function definition or application.
| Compare this OCaml code against its Python equivalent.
| > let hello_world h w = (h, w) > let h, w =
| hello_world 1 2 vs. > def
| hello_world(h, w): > return (h, w) > h, w =
| hello_world(1, 2) *) module Id : ID = struct
| type t = string let of_string s = s let
| to_string s = s end let main () =
| let s = "abc123" in let id = Id.of_string s in
| (* NOTE(type error): because the built-in "print_endline"
| function is of type (string -> unit) and not (Id.t -> unit)
| *) (* NOTE: if an expression returns unit, you don't
| need to create a let binding for it. You can simply tack a
| semicolon to the end of it if you need sequence another
| expression to follow it. *) print_endline id;
| (* okay *) (* STDOUT: abc123 *) print_endline
| (Id.to_string id) ;; main ()
|
| You could imagine implementing this pattern of defining
| parsers such as "of_string", "of_bytes", "of_json", "of_int",
| "of_db_row", "of_request", for any piece of input data. You
| can think of all of these functions as static constructors in
| OOP... you take in some data, and produce some output value:
| e.g. "of_string" takes in a `string` and produces a `t`.
|
| Now, if you have a bunch of "values" of type `t`, you know
| that they _only_ could have been produced by the `of_string`
| function, because `of_string` might be the _only_ function
| that ends with `-> t`. Therefore, all the values maintain the
| same properties enforced by the `of_string` function (similar
| to class constructors in OOP). With this, you can create
| types such as `Nonnegative.t`, `Percent.t`, `Currency.t`,
| `Image.t`, `ProfilePicture.t`, and parsers from another type
| to the newly minted type.
|
| The compiler can help you enforce these properties by
| providing guardrails in the form of static compiler checks
| (these checks are run _before_ your code can even be
| compiled). If I have a value of type `Nonnegative.t`, then
| not only do I not need to validate that it's not negative, I
| also don't have to validate that it's not negative everywhere
| else that values of that type are used -- the validation
| logic is baked into the constructor. Parse, don't validate.*
| churlin wrote:
| I have worked with Haskell, Scala, and OCaml; they all bring the
| joy of programming into daily tasks, and OCaml has a fast
| compiler and a great module system. This makes it a really fun
| and effective language to use.
| therealdrag0 wrote:
| Scala has some quirks but I enjoyed it relative to more popular
| languages and its apparent stagnation makes me sad.
| vips7L wrote:
| Scala just seems to have an ever changing identity. Scala 3
| drastically changed syntax and now they're trying to move the
| language from monads to effects.
| abathologist wrote:
| One of OCaml's outstanding, but too little mentioned,
| virtues is the community's commitment to extremely strong
| backwards compatibility guarantees.
| Quekid5 wrote:
| Which led to a _really_ bad standard library with next to
| no features... but yes, it 's very stable.
|
| And, hey, if it works for you, that's great... but
| Batteries Included can also be great for a language.
| Quekid5 wrote:
| Scala 3 didn't "drastically" change syntax -- most of the
| changes also have automatic rewrites with appropriate
| compiler switches. The effects story is still pretty
| experimental, but there's also improvements to 'effects'
| syntax (for-comprehensions) in "preview" for 3.7.
|
| As long as the 'effects' work will let me distinguish
| pure/non-pure, I'd be happy to use just that bit and stick
| with ZIO/TypeLevel's ecosystem... which will probably be
| supported forever, regardless of whatever happens with the
| "effects" stuff.
| Quekid5 wrote:
| The Ocaml module system is great, but the module system in
| Scala isn't the usual Java package thing... it's traits. It's
| about as powerful as the OCaml module system on any axis I've
| ever used, but it's easy to miss how powerful it is. (Scala 3
| added some ergonomics to make it easier to use, but it was all
| technically accessible in Scala 2 with 'workarounds'.)
| markstos wrote:
| My concern for a team language choice is "How hard is going to be
| be hire people to write in this language effectively and how much
| will /they/ enjoy it?"
|
| It's one thing to pick a language that I like and am productive
| in, it's another to choose a language for a larger team.
|
| If you've found an full team of motivated and capable OCaml
| coders, great.
| sausagefeet wrote:
| It has not been a challenging finding candidates for OCaml. For
| the most part, people who like OCaml are chomping at the bit to
| find a job writing it. And for those that don't know OCaml, a
| lot of really good devs are excited to try something different.
| a-french-anon wrote:
| Sorry for the large aside, but anyone knows the whereabouts of
| the Flambda2 project? Can't find the GH repo anymore, only this
| fork I didn't know about: https://github.com/oxcaml/oxcaml/
| debugnik wrote:
| That's the repo, Jane Street has rebranded their OCaml fork to
| OxCaml (as in oxidised, Rust-like). From the readme:
|
| > This is also the home of the Flambda 2 optimiser
|
| Their plan is to use OxCaml as their experimental fork and work
| with upstream to port features from it. Labelled tuples and
| immutable arrays for example landed in OCaml 5.4 but were
| originally from OxCaml.
| stonemetal12 wrote:
| >One operation can't corrupt another operation's view of state
| because state is immutable by default.
|
| How true is this in practice? I mean on the one hand sure
| Operation 2 doesn't seem some half modified state from Operation
| 1. On the other hand Operation 2 now has some stale state and
| makes the wrong decisions does the wrong thing because it didn't
| see Operation 1's changes.
| jinwoo68 wrote:
| That happens whether immutable or not. In the mutable world,
| you have to guard that using a mutex or something. In that
| case, operation 1 may be blocked by operation 2, and now you
| get a "stale" state from operation 2. But that's okay. You'll
| get a new state next time. The real problem occurs when two
| states are mixed and corrupted.
| baby wrote:
| I wrote the OCamlByExample and I can only say, good luck, I don't
| think OCaml is ready for production, and it's generally not a
| very user-friendly language, but IMO it's all about having fun
| first and if this is what makes it fun for you guys then you
| should do it!
|
| Also with LLMs it's probably easier to just feed the compiler
| errors to an LLM and get something readable at the end.
| abathologist wrote:
| All fine and good if you don't like it for whatever reasons,
| but AFAIK, people have been using OCaml software as parts of
| tech stacks in critical industries for around 20 years. So
| unless you have some qualifications to offer,
|
| > I don't think OCaml is ready for production
|
| seems to indicate your thinking is just not based on fact. This
| position is further belied by the stack of successful
| production applications you can see at
| https://ocaml.org/industrial-users/businesses.
| dzonga wrote:
| nicely designed site - welcome change from dark background,
| gradient colors etc.
|
| just white, grey & blue.
___________________________________________________________________
(page generated 2025-11-07 23:01 UTC)