Live data from Hacker News

OCaml as my primary language

xvw.lol

221–230 of 296 posts

Re: OCaml as my primary language

#221

Earlier quoted context omitted.

I have found that Haskell has two good things going for it when it comes to LLM code generation. Both have to do with correctness. The expressive type system catches a lot of mistakes, and the fact that they are compile errors which can be fed right into the LLM again means that incorrect code is caught early. The second is property based testing. With it I have had the LLM generate amazingly efficient, correct code,…

Property-based testing is available in other languages. E.g., JS has fast-check, inspired by quickcheck.

The way code is written in Haskell, small laser focused functions and clearly defined and mockable side effects, lends itself very well to property based testing.

This might not be impossible to achieve in other languages, but I haven’t seen it used as prevailently in other languages.

Re: OCaml as my primary language

#222
post #6

I haven't worked in OCaml but I have worked a bit in F# and found it to be a pleasant experience. One thing I am wondering about in the age of LLMs is if we should all take a harder look at functional languages again. My thought is that if FP languages like OCaml / Haskell / etc. let us compress a lot of information into a small amount of text, then that's better for the context window. Possibly we might be able to p…

If LLMs get a little better at writing code, we might want to use really powerful type systems and effect systems to limit what they can do and ensure it is correct. For instance, dependent types allow us to say something like "this function will return a sorted list", or even "this function will return a valid Sudoku solution", and these things will be checked at compile time --again, at compile time . Combine this…

The day when LLMs generate useful code with dependent types! That would be awesome!

Re: OCaml as my primary language

#223
post #216

Earlier quoted context omitted.

I agree. If OCaml had solved some of its bigger paper cuts it could have been a real player. Compilation time is much better than Rust too: * OPAM is quite buggy and extremely confusing. * Windows support is very bad. If you ever tried to use Perl on Windows back in the day... it's worse than that. * Documentation is terse to the point of uselessness. * The syntax style is quite hard to mentally parse and also not ve…

I'd say the Modula-2 inspired module system is a very valuable asset compared to today's Rust. The only contact with OCaml I had was that I wrote a bug report to a university professor because I wanted his tool to process one of my files, but the file was larger than OCaml's int type could handle. That itself wasn't the problem - he wrote it wasn't straight forward to fix it. (This is a bug of the type "couldn't have…

It is more the other way around ML predates Modula-2, and the module system like ideas were already present in Mesa and UCSD Pascal. :)

Re: OCaml as my primary language

#224

Question about terminology: Is it common to call higher-order function types "exponential types" as the article does? I know what higher-order functions are, but am having trouble grasping why the types would be called "exponential".

The answers in the replies are all good but the real reason is because in category theory the construct that models function types is called an "exponential product". The choice of that name stems from the reasons explored in the replies, in particular from the fact that the number of total functions from A to B is aka ways determined by an exponent (cardinality of B raised to the power of cardinality of A)

Re: OCaml as my primary language

#225

I’d have liked to see the use of dependency injection via the effects system expanded upon. The idea that the example program could use pattern matching to bind to either test values or production ones is interesting, but I can’t conceptualize what that would look like with the verbal description alone. Also, I had no idea that the module system had its own type system, that’s wild.

Haskeller here!

> The idea that the example program could use pattern matching to bind to either test values or production ones is interesting, but I can’t conceptualize what that would look like with the verbal description alone.

The article appears to have described the free monad + interpreter pattern, that is, each business-logic statement doesn't execute the action (as a verb), but instead constructs it as a noun and slots it into some kind of AST. Once you have an AST you can execute it with either a ProdAstVisitor or a TestAstVisitor which will carry out the commands for real.

More specific to your question, it sounds like the pattern matching you mentioned is choosing between Test.ReadFile and Test.WriteFile at each node of the AST (not between Test.ReadFile and Prod.ReadFile.)

I think the Haskell community turned away a little from free monad + interpreter when it was pointed out that the 'tagless final' approach does the same thing with less ceremory, by just using typeclasses.

> I’d have liked to see the use of dependency injection via the effects system expanded upon.

I'm currently doing DI via effects, and I found a technique I'm super happy with:

At the lowest level, I have a bunch of classes & functions which I call capabilities, e.g

  FileOps (readTextFile, writeTextFile, ...)
  Logger (info, warn, err, ...)
  Restful (postJsonBody, ...)
These are tightly-focused on doing one thing, and must not know anything about the business. No code here would need to change if I changed jobs.

At the next level up I have classes & functions which can know about the business (and the lower level capabilities)

  StoredCommands (fetchStoredCommands) - this uses the 'Restful' capability above to construct and send a payload to our business servers.
At the top of my stack I have a type called CliApp, which represents all the business logic things I can do, e.g.

I associate CliApp to all its actual implementations (low-level and mid-level) using type classes:

  instance FileOps CliApp where
    readTextFile  = readTextFileImpl
    writeTextFile = writeTextFileImpl
    ...

  instance Logger CliApp where
    info = infoImpl
    warn = warnImpl
    err  = errImpl
    ...

  instance StoredCommands CliApp where
    fetchStoredCommands = fetchStoredCommandsImpl
    ...
In this way, CliApp doesn't have any of 'its own' implementations, it's just a set of bindings to the actual implementations.

I can create a CliTestApp which has a different set of bindings, e.g.

  instance Logger CliTestApp where
    info msg = -- maybe store message using in-memory list so I can assert on it?
Now here's where it gets interesting. Each function (all the way from top to bottom) has its effects explicitly in the type system. If you're unfamiliar with Haskell, a function either having IO or not (in its type sig) is a big deal. Non-IO essentially rules out non-determinism.

The low-level prod code (capabilites) are allowed to do IO, as signaled by the MonadIO in the type sig:

  readTextFileImpl :: MonadIO m => FilePath -> m (Either String Text)
but the equivalent test double is not allowed to do IO, per:

  readTextFileTest :: Monad m => FilePath -> m (Either String Text)
And where it gets crazy for me is: the high-level business logic (e.g. fetchStoredCommands) will be allowed to do IO if run via CliApp, but will not be allowed to do IO if run via CliTestApp, which for me is 'having my cake and eating it too'.

Another way of looking at it is, if I invent a new capability (e.g. Caching) and start calling it from my business logic, the CliTestApp pointing at that same business logic will compile-time error that it doesn't have its own Caching implementation. If I try to 'cheat' by wiring the CliTestApp to the prod Caching (which would make my test cases non-deterministic) I'll get another compile-time error.

Would it work in OCaml? Not sure, the article says:

> Currently, it should be noted that effect propagation is not tracked by the type system

Re: OCaml as my primary language

#226

Earlier quoted context omitted.

I think "no gc but memory safe" is what originally got people excited about Rust. It's a genuinely new capability in production ready languages. However, I think Rust is used in many contexts where a GC is just fine and working with lifetimes makes many programs more painful to write. I think for many programs the approach taken by Oxidized OCaml[1] or Scala[2] gives 80% of the benefit while being a lot more ergonomi…

Spot on. It's also fascinating to watch people have their minds blown in 2020+ by basic features that have been around since the nineties. It's kind of sad, actually. The industry would be in such a better place than it is today if so many programmers weren't allergic to all things academic and "theoretical" and were more curious and technical than they were conceited. It's baffling that computing, literally a subjec…

And eventually nothing of it will matter because our AI overlords will eventually translate natural language, maybe with some added help from formalisms, into any kind of application.

Re: OCaml as my primary language

#227
post #171

Earlier quoted context omitted.

Considering how many applications are running in JS/Python execution speed or GC is a low concern for many programs. Ergonomics (community, compiler guarantees, distribution, memory pressure, talent availability, whatever) seem more meaningful.

I get your point, but JS is an order of magnitude faster than Python, they are not in the same league. A lot of effort went into making it efficient thanks to the web, while python sorta has its hands tied back due to exposing internals that can be readily used from C.

PyPy could get some community love, but I guess it will never happen, and on the GPU side, Python is basically a compiler DSL.

Re: OCaml as my primary language

#228

Earlier quoted context omitted.

LSP isn't the protocol that interfaces with debuggers, that'd be DAP. You're right that OCaml debugging is kinda clunky at the moment. OCaml does have an okay LSP implementation though, and it's getting better; certainly more stable than F#'s in my experience, since that comparison is coming up a lot in this comment section.

What’s clunky about the Ocaml debugger? Ocaml has been shipping with an actual fully functional reverse debugger for ages. Is the issue mostly integration with the debugging ui of VS Code?

it was clunky AF last time I tried to use it https://discuss.ocaml.org/t/debug-ocaml-code/10867/18

and yeah integrating to VS Code debugging UI would be ideal

I really like OCaml, so I hope the community can continue to improve the UX of these features

Re: OCaml as my primary language

#229

Earlier quoted context omitted.

> The moment you start ripping cases as distinct types out of the sum-type, you create the ability to side-step exhaustiveness and sum-types become useless in making invalid program states unrepresentable. Quite the opposite, that gives me the ability to explicitly express what kinds of values I might return. With your shape example, you cannot express in the type system "this function won't return a point". But with…

> Not sure about C#, but in Java if you write `sealed` correctly you won't need the catch-all throw. Will the compiler check that you have handled all the cases still? (Genuinely unsure — not a Java programmer)

Yes, that's the whole purpose of marking an interface/class `sealed`.

Re: OCaml as my primary language

#230

Earlier quoted context omitted.

Did you try F# in JetBrains Rider? It's the best F# tooling you can buy IMO.

Yes I actually ended up using Rider, although I don't like switching editors. But that only replaces Ionide, and I was having some growing pains with the entire toolchain.

Yeah - I worked on an F# project that was ~300K LoC and tooling speed really becomes an issue at that point.
Post reply on HN