[HN Gopher] OCaml Programming: Correct and Efficient and Beautiful
       ___________________________________________________________________
        
       OCaml Programming: Correct and Efficient and Beautiful
        
       Author : philonoist
       Score  : 278 points
       Date   : 2022-06-23 12:38 UTC (1 days ago)
        
 (HTM) web link (cs3110.github.io)
 (TXT) w3m dump (cs3110.github.io)
        
       | m_a_g wrote:
       | I learned some Haskell years ago but it didn't stick with me.
       | Lately, I want to get back into functional programming to see
       | whether I'm missing out on something. This book looks nice, and
       | so does OCaml. Any suggestions on books, courses, or resources on
       | this subject?
       | 
       | Also, just to get proper motivation, why should I learn
       | functional programming as a capable Software Engineer?
        
         | mejutoco wrote:
         | IMHO the main reason to learn functional programming is to
         | learn to untangle state and behaviour.
        
         | kubb wrote:
         | You can strengthen the skill of thinking about your programs in
         | terms of higher level abstractions, which allows you to have an
         | overview and reason about larger systems.
        
         | yakak wrote:
         | Dan Grossman's Programming Languages is a great 3 language
         | online course. It gives you the opportunity to compare standard
         | ML, ruby and racket.
         | 
         | Before that course I was already grouping languages by
         | inheritances from C or Lisp families, but really started to
         | recognize and appreciate ML more.
        
         | bhrgunatha wrote:
         | Berkeley's Programming Languages and Compilers references this
         | book and you get to apply OCaml to something non-trivial.
         | 
         | https://inst.eecs.berkeley.edu/~cs164/fa21/
        
           | tgflynn wrote:
           | It doesn't appear from your link that the course materials
           | are available online though.
        
             | bhrgunatha wrote:
             | On the schedule page, there are links to further material.
             | I don't think you can do the drills or exams, but you _can_
             | do the homework if you have a github account.
        
         | tremon wrote:
         | You should learn multiple paradigms to broaden your horizon, so
         | you are able to approach a problem from multiple angles and
         | decide on the best design approach.
         | 
         | I don't write in a functional language in my day job, but I'm
         | much more aware of global state and side-effects in my code
         | than before. We mainly write in Python and C#, but I encourage
         | my team members to dabble in SQL (for dataset manipulation) and
         | Haskell (for functional purity). If anything, it makes them
         | understand LINQ better.
        
         | ufo wrote:
         | For Ocaml in particular, one thing that is pretty cool about it
         | is the module system. Although we unfortunately can't use this
         | module system in other languages, it taught me some interesting
         | lessons about abatract data types.
        
         | ufo wrote:
         | Another thing I recommend everyone to learn at least once is
         | sum types aka algebraic data types. And how they are the dual
         | to class based dispatch (what some call the "expression
         | problem"). Sum types aren't exclusively found in functional
         | languages, but they are certainly prominent in them.
        
         | yawaramin wrote:
         | The OP is a great book to learn OCaml. There is also a
         | corresponding set of lecture videos on YouTube.
        
       | water8 wrote:
       | OCaml Sucks and I'm almost certain this is the same professor who
       | was at UCLA beating this horse to death at least 10 years ago. It
       | has no performance or coding benefit for teams larger than a few.
        
         | tgflynn wrote:
         | > It has no performance or coding benefit for teams larger than
         | a few.
         | 
         | That must be why Jane Street uses it then.
        
           | darthrupert wrote:
           | And pretty much only them.
           | 
           | Perhaps some day we'll learn how little the programming
           | language matters for anything beyond coding.
        
             | gjadi wrote:
             | When you look for job in OCaml, you can find several
             | fintech and blockchain related companies.
        
         | cmrdporcupine wrote:
         | I'm so glad you took time out of your day to come here and tell
         | us this.
        
       | blackerby wrote:
       | A quick scroll through the comments and it looks like no one is
       | actually talking about the book itself, so I will. I came across
       | it at a really good time in my computer science self education.
       | It helped me learn about some essential data structures and
       | algorithmic analysis. The video lectures interspersed in the text
       | augment the text and vice versa. The sections without videos were
       | tougher for me to digest, but are written clearly and bear
       | revisiting and study. If you, like me, are looking for a good
       | next step after Grossman's PL MOOC, I recommend spending some
       | time with this incredible free resource.
        
         | haskellandchill wrote:
         | I'm enjoying the discussion of commutative diagrams,
         | abstraction functions, and representation invariants in section
         | 6.3. It is very well motivated and easy to understand.
        
       | kubb wrote:
       | My introduction to programming course in my university was in
       | OCaml. It was all downhill from there when it comes to the
       | programming languages I had to use.
        
         | newsoul wrote:
         | Which university? Does the course have a public webpage?
        
           | pwiecz wrote:
           | Not the original commenter, but my first year course of
           | Introduction of Programming 15 years age was also using
           | OCaml. There were two groups of students, standard one was
           | using Pascal, and the functional one using OCaml. I was
           | studying on Warsaw University and the lecture notes are in
           | Polish: https://mimuw.edu.pl/~kubica/wpf/wpf.pdf
        
             | newsoul wrote:
             | Thanks! 15 years, that's quite a long time ago.
        
               | pwiecz wrote:
               | Yup. I really loved all the functional stuff I've learned
               | there from OCaml to SML+Extended ML, to Haskell.
               | 
               | Unfortunately, I haven't had a chance to professionally
               | program functionally since the n. :(
               | 
               | It was all Java and C++. Though I like C++, maybe it's a
               | Stockholm syndrome. ;)
        
             | kubb wrote:
             | That was exactly the course that I took at the WU :) Fond
             | memories.
        
           | lmm wrote:
           | Cambridge does this (or did when I was there);
           | https://www.cl.cam.ac.uk/~lp15/MLbook/ is the book for the
           | course (available online).
        
         | afarviral wrote:
         | Seems its better to start with javascript so you can have the
         | reverse experience.
        
           | kubb wrote:
           | Then I would probably be writing HN posts claiming that
           | languages with advanced type systems and functional
           | programming are too hard and impractical, and that they are
           | trying to be clever and cool for no reason.
           | 
           | But, partly thanks to that course, I'm able to pick up any
           | paradigm, and my opinion on what's better is informed by
           | knowledge of both things. I'm also able to structure
           | imperative code better than my "imperative only" colleagues.
        
           | bobthechef wrote:
        
       | zaptrem wrote:
       | I took this class last semester. Michael Clarkson is an awesome
       | professor. After learning OCaml I want pattern matching in
       | JavaScript! Beyond the language itself, it absolutely made me a
       | better programmer.
        
         | toastal wrote:
         | Pattern matching without ADTs though loses a lot of the power.
         | The fact that you'd still have to handle null/undefined cases
         | means it's still annoying to use and hardly exhaustive.
        
           | 0x69420 wrote:
           | erlang/elixir programmers would disagree -- we match on ad-
           | hoc dynamically typed tuples that serve broadly the same
           | purpose as ADTs all the time, where the only exhaustiveness
           | you get is the degenerate case ie `_ -> shit_the_bed()`
           | 
           | especially common are pattern-matching assignments
           | (technically `=` _is_ the "match operator") that
           | idiomatically behave much like matches on the left hand side
           | of ` <-` in haskell do notation, i.e. inline runtime
           | assertions on structure
           | 
           | these communicate programmer intent really well! you could
           | probably have them in static land in a non-`monadFail`
           | context too, but you'd want dependent types or something lest
           | you go the way of typescript and resign yourself to
           | unsoundness
           | 
           | in fact it's so nice that i feel pain in any dynamic language
           | without full-fat pattern matching, like when i have to write
           | disgusting if-else chains in complex nix expressions
        
             | franz_kafkaagh wrote:
             | I write Elixir for work and I love most things about the
             | language.
             | 
             | The pattern-matching would still be made better with static
             | checks. It sucks to have a function blow up at runtime
             | because of something as trivial as args being swapped or
             | some code somewhere changing its return types. Someone
             | brought up Gleam earlier, but god why with that syntax?
             | 
             | I'd still rather have dynamic checked pattern matching over
             | the alternative.
        
               | 0x69420 wrote:
               | dialyzer and a liberal sprinkling of typespecs gets a
               | decent chunk of the way there, meanwhile the problem with
               | gleam and friends is the fact that they're altogether new
               | languages -- the pragmatic solution would be a restricted
               | subset of erlang (that a likewise restricted subset of
               | elixir could comfortably compile down to) where your
               | module _has_ to be fully typed, but you would need some
               | pretty gnarly logics to handle things like  "yes, when
               | this function mashes these two iolists together in this
               | way, it's still an iolist" etc
        
       | pjfin123 wrote:
       | I took this class!
        
       | mark_l_watson wrote:
       | That is a great format for an online book! The text looks like it
       | could be read on its own for a quick review, and the hundreds of
       | short embedded videos dig into detail.
        
       | usrn wrote:
       | I really want to like ML but I have some complaints about OCaml
       | in particular:
       | 
       | The stdlib is _very_ weak so everyone uses this third party
       | library. That really rubs me the wrong way.
       | 
       | All structures share a namespace for their elements which is
       | super wacky.
        
       | sshine wrote:
       | I spent a lot of time on Standard ML in university.
       | 
       | OCaml was always portrayed as the engineer's alternative for real
       | applications.
       | 
       | I spent some time brushing up on OCaml a few years ago using
       | Exercism.io.
       | 
       | While OCaml is a personal "top tier" language, I'd always prefer
       | Haskell, Rust or Scala.
       | 
       | Type classes / traits just seem to beat a higher-order module
       | system for me.
        
         | cmrdporcupine wrote:
         | I learned SML/NJ and OCaml around the same time and for some
         | reason I found SML more pleasant. I particularly liked the SML
         | "Basis" library, it felt really well designed to me.
         | 
         | Still, I'd pick either over Scala. Superior compilation times,
         | cleaner syntax, less complex. Haskell also just feels
         | excessively clever.
         | 
         | I had hoped Rust would be "OCaml but for systems programming"
         | and it sort of is, -- but the borrow checker and memory safety
         | features, as neat as they are, add a lot of mental overhead.
        
           | frou_dh wrote:
           | SML definitely has a more tastefully designed syntax also,
           | and not only because it's a smaller grammar. OCaml syntax is
           | the "scuffed" version of SML syntax, as the kids say!
           | 
           | Unfortunately to be a real-world SML user these days is a
           | very isolating proposition, because although there are some
           | really nice implementations (MLton, PolyML), to a first
           | approximation there are zero libraries.
        
             | xvilka wrote:
             | SML is the dead end because specification wasn't updated
             | for decades.
        
         | xyproto wrote:
         | I also find it hard to spot why OCaml is the natural "step up"
         | for creating real world applications instead of Haskell, Rust,
         | Clojure or even Kotlin, C++, Python and Go.
        
           | toolslive wrote:
           | let's see:                   - OCaml vs Haskell: eager vs
           | lazy  (=> memory consumption is more predictable)         -
           | OCaml vs Rust: OCaml has a GC (=> comfort) OCaml has tco
           | (could not resist this ;) )         - OCaml vs Clojure:
           | vastly superior typing system. (=> less bugs)         - OCaml
           | vs Kotlin: no JVM needed.         - OCaml vs C++: more
           | safety. once it compiles it will not segv.         - OCaml vs
           | Go: vastly superior typing system. (=> less bugs)         -
           | OCaml vs Python: vastly superior type system, way better
           | performance.
           | 
           | The biggest risk of doing OCaml (or Haskell or Rust) for
           | extended periods of time is that you will be unable to hide
           | your feeling of superiority towards (fe) a python developer.
        
             | contificate wrote:
             | I think the languages you selected in your final remark sum
             | it up for me. If one is truly taken by functional
             | programming, much of their mental model starts to revolve
             | around algebraic (inductively defined) data types and
             | structural recursion (aided by pattern matching) over them
             | - such that mentally reducing the set of candidate
             | languages really does become an implicit process of
             | questioning: "does X have ergonomic, statically-typed,
             | discriminated sums?".
             | 
             | Lots of mainstream languages simply fail this test and make
             | it feel like intellectual poverty or that there's extreme,
             | turgid, boilerplate required for a weak imitation of the
             | features (see the idiomatic class-hierarchy encoding of
             | ADTs in large projects - such as LLVM - in C++, for
             | example).
             | 
             | It's absolutely no surprise that more mainstream languages
             | are picking up a match-like construct and lighter encodings
             | of discriminated sums. So, it really comes to what else you
             | wish to be burdened with when compiling an OCaml-like
             | mental model to X in your head: Tagged unions a-la C? Class
             | hierarchies for ADTs in C++? The travesties of
             | std::variant? Monad transformers in Haskell? Lazy
             | evaluation? No static typing at all? Caring about memory
             | management and ownership? Box and Arc-ing recursive
             | components of ADTs? Writing your own arena allocator?
             | Compiling to the JVM? Spotty TCO support?
             | 
             | OCaml is just a nice, fairly simple (at its core, at
             | least), language that captures the essence of the ML
             | family, compiles to native (and bytecode and, transitively,
             | JavaScript), has great tooling (opam, dune, ocamllex,
             | memhir, etc.), great libraries (official LLVM bindings, for
             | example), and a great community. Lots of OCamlers are well
             | aware of other potential languages that somewhat suit their
             | style of programming, they just don't want to be burdened
             | by the other stuff.
        
               | [deleted]
        
             | water8 wrote:
             | OCaml vs All: Good luck finding developers that will write
             | quality code and not cost a fortune each.
        
               | yawaramin wrote:
               | If Jane Street can teach OCaml to traders, I can teach it
               | to developers. That's not a concern for anyone other than
               | a sweatshop.
        
               | akhmatova wrote:
               | _If Jane Street can teach OCaml to traders, I can teach
               | it to developers._
               | 
               | You can if they are motivated to learn. Unless you are
               | offering Jane Street levels of compensation; and in
               | return, the candidate is willing to believe, or pretend
               | to believe that company's shtick about OCaml being so
               | categorically superior as a general-purpose development
               | language so as to leave all the others in the dust --
               | most likely they won't be.
        
               | yawaramin wrote:
               | Or if, you know, they were hired to work on a project
               | where they get paid a salary. That seems to incentivize
               | most devs.
        
               | akhmatova wrote:
               | Not "a salary", but a Jane Street salary and resume cred.
               | Otherwise you just won't find that many takers.
        
               | toolslive wrote:
               | Actually, what I found more impressive is that they also
               | got them to use emacs.
        
               | akhmatova wrote:
               | It's not getting things done that matters. It's feeling
               | superior to others that matters.
        
               | ladis_washerum wrote:
        
               | shikoba wrote:
               | Hey, it's not a feeling. It's a fact.
        
               | hutzlibu wrote:
               | It is certainly a fact, that you feel superior.
               | 
               | I would like to confirm that fact, by comparing
               | productive output..
        
               | wiseowise wrote:
               | Define "productive output".
        
             | frizlab wrote:
             | What about Swift?
        
               | Zababa wrote:
               | On the server, less ecosystem than OCaml. Long compile
               | times. A big part of its "market share" is taken by Rust
               | instead.
        
               | KurtMueller wrote:
               | For now, a good language if you want to develop apps for
               | the mac ecosystem.
        
             | twh270 wrote:
             | Not sure what you mean by "- OCaml vs Kotlin: no JVM
             | needed." as Kotlin has support for native and JavaScript
             | compilation, and WASM via native.
        
               | wiseowise wrote:
               | Did you even try it? It still requires Java, Gradle and
               | Kotlin compiler to work. Also, good luck making it work
               | without using Intellij.
        
               | toolslive wrote:
               | so it's either a JVM or either no libraries?
        
               | pjmlp wrote:
               | Kotlin has a crude support for native, and they had to
               | reboot the implementation as they got clever and went
               | with a memory model incompatible with JVM and JS GCs,
               | thus making code portability an headache.
        
               | thayne wrote:
               | Maybe it's changed in the year or so since I looked at
               | kotlin, but my impression when I did look at it was
               | support for anything other than the JVM was definitely
               | second class
        
             | 0des wrote:
        
             | throwamon wrote:
             | > vastly superior typing system. (=> less bugs)
             | 
             | I have no horse in this race, but claiming it to be "vastly
             | superior" and implying "less bugs" makes it sound like this
             | is a logical consequence, when you're actually staying on
             | one side of an endless debate that to me doesn't have clear
             | winners. For instance, from the little I've learned about
             | Clojure, they claim the lack of a "vastly superior type
             | system" is a feature, not a bug, and it's a result of a
             | fundamental difference in some beliefs about how to write
             | correct software.
             | 
             | From this I wonder how misrepresentative your other
             | comparisons are as well. But don't get me wrong, OCaml is
             | probably my "favorite language I've never actually used"
             | (I've never "actually used" Clojure in "real projects"
             | either).
        
               | toolslive wrote:
               | Take any open source python project on github (or others)
               | look at the list of issues and count the number of
               | 'NoneType' has no attribute ... instances. All these
               | could have been avoided by a decent type system. I rest
               | my case ;)
        
               | tgflynn wrote:
               | I don't think I've ever seen anyone seriously argue
               | against the claim that strong typing systems at least
               | prevent many types of bugs. Now people may think that
               | they are more productive in a language with weak types
               | but that's a different consideration.
        
               | camgunz wrote:
               | This was on HN the other day:
               | https://github.com/hwayne/awesome-cold-showers#static-vs-
               | dyn.... I would probably add a caveat to the listed
               | caveats that testing might not be considered? Like if
               | every dynamically typed code base implements an ad-hoc
               | typechecker with a testing framework, it's a distinction
               | without a difference.
        
               | laserlight wrote:
               | I've passed wrong type of arguments to Python functions
               | and assumed type of the return value wrong countless of
               | times. That has never happened in Haskell. I don't know
               | how to reconcile my experience with the statement that
               | there's no evidence that strong typing reduces bugs.
        
               | camgunz wrote:
               | I program primarily in (untyped) Python (which, I get is
               | technically strongly typed but people don't think in
               | technical terms) these days, and I almost never
               | experience type errors. I guess I could attribute this to
               | a couple things:
               | 
               | - I'm very specific about when I use None
               | 
               | - I'm a big fan of named function arguments
               | 
               | - I like to think my naming of things is pretty good, as
               | are my conventions for parameters
               | 
               | - I try to handle all possible cases (what I mean here is
               | I do and if I don't I made a mistake)
               | 
               | I use tests very sparingly in personal projects, but yet
               | I haven't really felt their absence. If I ever write a
               | piece of particularly hairy code (metaprogramming comes
               | to mind... lord) I'll write a quick script testing some
               | cases and then delete it.
               | 
               | Anyway, all that is to say I think part of dynamic
               | programming is you build an immune system for this stuff.
        
               | pharmakom wrote:
               | I think the big win is actually immutable data and pure
               | functions. It just so happens that strong typing tends to
               | come along with these things (e.g. Haskell, OCaml)
        
       | tgflynn wrote:
       | This is a very specific question but I thought I'd ask it here on
       | the chance someone might have a good answer.
       | 
       | I've been slowly working my way through this course and I ran
       | into an issue recently when I upgraded my OCaml installation to
       | the latest version (4.13.1). With this version I find that the
       | simple instructions for building an executable with dune
       | (especially one that uses OUnit) given here
       | https://cs3110.github.io/textbook/chapters/data/ounit.html no
       | longer work.
       | 
       | It seems dune now requires you to initialize a project. Does
       | anyone know how to translate the instructions in the course to
       | the current version of dune ?
       | 
       | I realize I could install an opam switch for the previous version
       | but I'd rather be working with the latest one.
       | 
       | Just hoping someone might be able to save me an hour or so
       | figuring this out on my own.
        
         | mc10 wrote:
         | FYI: If you have more questions, the OCaml forum is very
         | friendly and helpful: https://discuss.ocaml.org/
        
         | octachron wrote:
         | You can add a dune-project file at the root of your project
         | with just `(lang dune 3.2)` (or your dune version rather than
         | 3.2). The file can also be generated by "dune init project
         | project_name" for fresh projects.
        
           | tgflynn wrote:
           | Thanks. It looks like all I needed was the project file. I
           | was confused because when I followed the instructions to run
           | dune init it created an entire directory structure and then I
           | was confused about where my code should go (I haven't gotten
           | to the chapter on modules yet).
        
         | shikoba wrote:
         | The latest version is 4.14.0. Personally I write my Makefile
         | myself and use them to build my projects. It works perfectly.
        
       | christophilus wrote:
       | I love OCaml. Fast builds, native binaries, fairly expressive and
       | elegant once you're accustomed to it. Now that it is multi-core,
       | I hope it gains traction.
        
       | raverbashing wrote:
       | "beautiful"
       | 
       | But then it has quirks like using ;; to end statements (EDIT:
       | only in the REPL). And comments with that weird syntax
       | 
       | But worse of all are the optional parenthesis in function calls.
       | Yes, I know Ruby has it. But it feel super weird and a needless
       | flexibility (that causes more confusion than it solves).
       | 
       | I can't get over this stuff, sorry.
        
         | toolslive wrote:
         | > worst of all are the optional parenthesis in function calls
         | 
         | ??                   > let r = some_function x y z in ...
         | 
         | There are _no_ parentheses here, and it 's not optional. What
         | you probably are missing is that if `some_function` takes 3
         | arguments, then `some_function x y` is a function value that
         | takes 1 parameter (currying).
         | 
         | Btw, iirc, SML doesn't have this: there you only have 1
         | parameter but it could be a tuple
        
           | frou_dh wrote:
           | > Btw, iirc, SML doesn't have this: there you only have 1
           | parameter but it could be a tuple
           | 
           | Nah, SML does also support automatically curried function
           | definitions, it's just that by convention that feature didn't
           | get used as often. I tried to find out why that was the case
           | a while ago, and stumbled on this explanation: https://www.re
           | ddit.com/r/ProgrammingLanguages/comments/jde9x...
        
           | raverbashing wrote:
           | > Note how OCaml is flexible about whether you write the
           | parentheses or not, and whether you write whitespace or not.
           | 
           | from here https://cs3110.github.io/textbook/chapters/basics/t
           | oplevel.h...
        
             | toolslive wrote:
             | isn't that the case in most programming languages ? In
             | essence, if you have an expression _exp_ then _(exp)_ is
             | also an expression. So nothing special about OCaml in that
             | regard. [note, just checked python, php, and js and it is
             | the case]
        
               | raverbashing wrote:
               | Ok so maybe the example is confusing, because it seemed
               | it was talking about function calls (where they are
               | mandatory in the languages you mentioned)
        
         | jon_smark wrote:
         | I have to interject here for the sake of those unfamiliar with
         | OCaml and who may take the parent comment at face value.
         | 
         | Saying "it has quirks like using ;; to end statements" is
         | misleading to the point of just being bogus. The double semi-
         | colon is only ever used in the REPL. In fact, I've been
         | programming OCaml for fun and professionally for over 15 years,
         | and I've never used a double semi-colon in my code, nor have I
         | ever encountered one in the "wild".
        
           | raverbashing wrote:
           | Thanks for clarifying
        
       | lairv wrote:
       | Every time I use recent "functional" languages (Rust, modern
       | Typescript) I realise how great OCaml is
        
         | munchler wrote:
         | Have you tried F#?
        
           | lairv wrote:
           | Nope, I must say I have only used a small subset of languages
           | from the FP-language-zoo, so my opinion on OCaml might be
           | biased
           | 
           | But as an example when writing Typescript, I feel so
           | frustrated of not having a clean way of doing pattern-
           | matching
        
             | pjmlp wrote:
             | You will have to wait for JavaScript to add it first.
             | 
             | The whole point of Typescript and why it is so successful,
             | is because they only add type system on top of JavaScript.
             | 
             | Pattern matching would introduce new language constructs
             | beyond what is required for defining types.
        
               | sn9 wrote:
               | Typescript gets compiled to Javascript anyway. What's
               | stopping any pattern matching construct from being
               | compiled to vanilla JS?
               | 
               | We already get stuff like type narrowing and generics.
        
               | pjmlp wrote:
               | The philosophy of not adding language constructs beyond
               | what is required for the type system.
               | 
               | Typescript code without type annotations should be
               | JavaScript compatible, minus features still in flight for
               | standardisation.
               | 
               | There are other languages for that like ReasonML.
        
       | jcelerier wrote:
       | I did a lot of my PhD work in OCaml. I hated pretty much every
       | minute of it compared to C++, when comparing the exact same
       | algorithms aha.
        
         | immigrantheart wrote:
         | Oh wow, interesting. Could you elaborate why?
        
       | muglug wrote:
       | I found my experience trying to work with a large OCaml base a
       | nightmare -- when signatures changed in an unstable dependency
       | (e.g. function argument removed and nested inside another), the
       | errors spat out by the typechecker were utterly
       | incomphrehensible.
       | 
       | This was largely due to automatic currying in OCaml -- if I have
       | a function call "some_function arg1 arg2" and "some_function"
       | adds a third argument, that call becomes a function that requires
       | a single argument, arg3, but the typechecker message that tells
       | you all of this is well-nigh unintelligible.
       | 
       | Switching to Rust was a blessed relief, not least because Rust
       | has much better developer tooling and documentation (but also no
       | automatic currying).
       | 
       | It made me think that OCaml is efficient and beautiful if you're
       | the only person touching your specific codebase (which I think is
       | true in the vast majority of cases) _or_ if many of your
       | colleagues are deep OCaml officionados with PhDs, but it 's not a
       | good collaborative language for the rest of us.
        
         | wyager wrote:
         | There are many similar warts in the ocaml type system.
         | Parametric polymorphism in ocaml sucks as well.
         | 
         | If you haven't tried haskell, you probably should. Its type
         | system is much cleaner (and more powerful, if you want) than
         | ocaml's, and many details of Rust are derived from Haskell.
        
           | abathologist wrote:
           | Haskell is a great language but has plenty of its own warts.
           | The value prop and trade offs between OCaml and Haskell
           | really makes it hard to use one as a drop in for the other
           | imo.
        
             | wyager wrote:
             | Strong disagree; I have thousands of hours with both and I
             | would essentially never recommend ocaml over haskell unless
             | your company already has an ocaml codebase/ocaml expert
             | employees.
             | 
             | I'm very conscious of the existence of pareto tradeoffs; I
             | am asserting that, in this case, there is essentially no
             | tradeoff to be made. Haskell is equal or better (sometimes
             | significantly so) in almost every relevant domain. For
             | domains in which ocaml is a better choice, it is not the
             | globally best choice (i.e. both ocaml and haskell are bad
             | in those domains).
        
               | substation13 wrote:
               | What about the performance challenges caused by lazy
               | evaluation?
        
               | belmont_sup wrote:
               | Fresh in both languages. But on studying the Effective
               | Haskell book, there's a whole chapter (chp 14) on
               | learning how to read and write efficient and faster
               | Haskell code. Training that mental model of understanding
               | how IO gets evaluated didn't seem too difficult. It feels
               | akin to remembering how to write fast SQL code - you just
               | practice a bit and measure.
               | 
               | In chp 7 on understanding IO, there's also another great
               | section that explains how IO evaluation is often
               | confused. I share an example from the book that reads,
               | writes, and prints files.                   Memory
               | intensive func because it attempts to read _all_ files at
               | once because the execution of reading and writing
               | actually only happens when the print fn is called
               | (putStrLn files). makeAndReadFile is actually doing both
               | reading and writing - a normal task in all other
               | languages.              slow =             let  files =
               | mapM makeAndReadFile [1..500] ::  IO  [ String ]
               | in  files >>= (putStrLn . show)
               | Efficent version. Here we force reads and writes to
               | actually occur per file instead of waiting til the print.
               | Seems like a pretty easy step to misunderstand.
               | safe ::  IO  ()  safe =             foldl ( \ io id ->
               | io >> makeAndShow id  ) (return ()) [1..500]
        
               | marcosdumay wrote:
               | Most people don't even get to see those challenges. But
               | make wide use of the many performance opportunities
               | created by lazy evaluation. Anyway, when any problem
               | appears, it is obvious, so if it was a large problem, it
               | would be fixed by now. The only reason people keep
               | talking about it is because it nearly never appears.
               | 
               | Really, lazy IO is a much larger source of problems, and
               | even there, after a week or two writing IO people just
               | learn to write code that doesn't break due to it.
        
               | wyager wrote:
               | People fixate on this, and all I can say is
               | 
               | * it has never been an issue for me
               | 
               | * I don't think the performance implications are nearly
               | as hard to understand as often implied
               | 
               | * in the extreme you can just enable the STRICT language
               | pragma in your project and forget about it :)
        
               | abathologist wrote:
               | That's funny. This has been a problem for every
               | experienced Haskell programmer I've known who works in
               | Haskell professionally. I wonder what domain you work in
               | where performance and space leaks haven't been a concern?
        
               | wyager wrote:
               | It's not that space leaks are not a concern; it's that
               | it's actually quite easy to avoid creating them, and if
               | you manage to do so and it becomes a problem, it's
               | usually easy to track down. I don't know any experienced
               | haskell programmer who would even mention this in a list
               | of complaints about the language (which they are sure to
               | have, but they will likely be more abstract).
        
               | yawaramin wrote:
               | Compile times and architecture astronomy are obvious
               | counterarguments to the 'Haskell is always superior'
               | delusion ;-)
        
               | abathologist wrote:
               | >Haskell is equal or better (sometimes significantly so)
               | in almost every relevant domain. For domains in which
               | ocaml is a better choice, it is not the globally best
               | choice (i.e. both ocaml and haskell are bad in those
               | domains).
               | 
               | I think this assertion is false, but it would take a lot
               | of careful evaluation to prove it one way or the other.
               | The most salient counterexample that comes to mind is
               | modularity and namespacing.
               | 
               | Of course it's possible I'm wrong, and Haskell is
               | objectively superior in every regard. But the OCaml
               | language and ecosystem has [a decades long track record
               | of stable and evident success
               | stories](https://ocaml.org/industrial-users) and I find
               | it well suited for the kinds of problems I like to
               | tackle. I also like the prevailing vibe in the (still)
               | small community.
               | 
               | This exchange reminds of a difference I've observed in
               | the cultural tendencies in the OCaml and Haskell
               | ecosystems: in my experience, OCamlers tend to not be
               | very invested in arguing for the supremacy of OCaml.
        
               | wyager wrote:
               | > but it would take a lot of careful evaluation
               | 
               | This is what I was doing for a number of years.
               | 
               | > The most salient counterexample that comes to mind is
               | modularity and namespacing.
               | 
               | Polymorphic variants and ocaml namespacing are nice,
               | granted. These are the two ocaml features I've ever
               | missed while using haskell. Minor details overall though.
               | Namespacing is not as useful with typeclasses and PVs
               | have typing problems.
               | 
               | > in my experience, OCamlers tend to not be very invested
               | in arguing for the supremacy of OCaml
               | 
               | Yeah, this is mostly because it's not as much of a
               | marginal improvement, so it doesn't have as many people
               | interested in shilling it.
        
           | octachron wrote:
           | Parametric polymorphic is the same in OCaml and Haskell. Did
           | you meant to say that the value restriction does not play
           | nicely with point-free programming?
        
             | wyager wrote:
             | That's one aspect of it. The ergonomics are dogshit. I
             | remember having to use explicit quantification all the time
             | when writing polymorphic library code. Also, without
             | typeclasses, polymorphism is super inconvenient, to the
             | point where it's almost exclusively used in the most
             | critical data structures like sets and maps. It's not "the
             | same as in haskell" except maybe in the very vague sense
             | that they have similar underlying type theories (although
             | ocaml's is much weaker - e.g. I remember needing to use
             | some hacks to approximate HKTs, while haskell handles them
             | easily).
        
               | octachron wrote:
               | OCaml does not require more annotations than Haskell for
               | polymorphic functions? Both language only require
               | annotations in the case where inference would be
               | undecidable (polymorphic recursions, higher-rank
               | polymorphism, and GADTs).
               | 
               | I know few cases where OCaml require less annotations
               | than Haskell, I would expect the reverse to be true.
               | 
               | And maps and sets are not the main use case for
               | polymorphism in OCaml. Even taking in account that it
               | sounds like you are talking about functors, maps and sets
               | are still not the only instance of functors in OCaml.
        
               | wyager wrote:
               | > OCaml does not require more annotations than Haskell
               | for polymorphic functions?
               | 
               | A) I am fairly confident this is not actually true in a
               | technical sense, although it's been several years since
               | I've thought about it
               | 
               | B) In any case, in the (many) instances where you (by
               | rule or convention) need to put a type signature on your
               | function (e.g. because it's a top-level function, one
               | that you export, etc.), it is a pain in the ass to make
               | it polymorphic in ocaml compared to in haskell.
               | 
               | > Even taking in account that it sounds like you are
               | talking about functors
               | 
               | No, I'm not, although ocaml people tend to think
               | exclusively in "functors" (badly named, in my opinion -
               | conflicts with the more common category-theoretic
               | definition) because the experience of using actually
               | parametric functions is so bad that they almost never do
               | it.
        
               | octachron wrote:
               | Do you have any examples? Unfortunately from my
               | perspective, I cannot make sense of your statements.
               | 
               | OCaml typing is principal in all situation where type
               | inference is decidable. The syntax for annotation for
               | polymorphic functions is isomorphic between OCaml and
               | Haskell.
               | 
               | If I take a random module in the standard library, let's
               | say Array, 95% of the functions in this module are
               | parametric polymorphic. I am thus genuinely puzzled by
               | your statement that "OCaml programmers almost never write
               | polymorphic functions".
        
               | wyager wrote:
               | I wrote up a bunch of grievance examples for a blog post
               | I never got around to publishing, but I'm traveling at
               | the moment so can't pull up my laptop. I'll see if I can
               | find them later.
               | 
               | > The syntax for annotation for polymorphic functions is
               | isomorphic between OCaml and Haskell.
               | 
               | Sorry, but total bullshit. I had to use these pieces of
               | shit all the time.
               | https://v2.ocaml.org/manual/locallyabstract.html
               | 
               | > If I take a random module in the standard library,
               | let's say Array
               | 
               | As I _specifically_ called out, all the core data
               | structures are polymorphic. (Let 's not talk about the
               | float array hack; is that still around?)
               | 
               | It's so annoying that pretty much every other library is
               | monomorphic or functorized.
               | 
               | > I am thus genuinely puzzled by your statement that
               | "OCaml programmers almost never write polymorphic
               | functions".
               | 
               | Not sure what to tell you man. I spent 4 years reading &
               | writing ocaml and 95% of the code that would have been
               | polymorphic in haskell (because it would be easy/free)
               | was either monomorphic or (multiple layers of) functors.
               | Many/most of the devs I worked with (great devs with
               | years of OCaml experience) didn't even know what LATs
               | were, let alone used them, which means they were almost
               | certainly not writing polymorphic code which "did
               | anything" with the type. The only polymorphic code that
               | you can write in ocaml without such things are functorial
               | (in the categorical sense, not the ocaml sense). Arrays,
               | map values, that's about it.
        
               | octachron wrote:
               | > Sorry, but total bullshit.
               | 
               | Ok, locally abstract types are bit weird and historical
               | quirk due to the pre-existing use of type variables as
               | unification type variables. But first, they only matter
               | with GADTs and local modules (a notion that doesn't exist
               | in Haskell). And the good syntax for polymorphic function
               | with GADTs is `type a. a monoid -> a` which requires just
               | one explicit quantification compared to the Haskell
               | variant. So no, Haskell and OCaml type annotations are
               | isomorphic.
               | 
               | > As I specifically called out, all the core data
               | structures are polymorphic.
               | 
               | I took a totally random module from the standard library!
               | Let me another random module after rolling a dice,
               | Atomic. Here only 66% of the functions are polymorphics.
               | Or do you want me to go to another library? Ok, let's go
               | for container, and let's select another random module
               | CCPair: 100% of the functions are polymorphic. Honestly,
               | I struggle to understand how it is possible to conclude
               | that every OCaml library is monomorphic.
               | 
               | What do you mean by LATs? The Haskell wiki doesn't seem
               | to know that term.
               | 
               | > The only polymorphic code that you can write in ocaml
               | without such things are functorial
               | 
               | I am sorry to ask but are you confusing bounded
               | polymorphism with parametric polymorphism?
        
               | wyager wrote:
               | > they only matter with GADTs and local modules
               | 
               | Local modules (assuming this means e.g. a module passed
               | in as a function argument, constrained to have one of its
               | types match the type of another argument) are how you
               | write non-trivial polymorphic code.
               | 
               | > Atomic ... CCPair
               | 
               | More functors (in haskell terminology) - code that
               | doesn't do anything interesting with the type parameter.
               | 
               | > What do you mean by LAT
               | 
               | Locally abstract type
               | 
               | > I am sorry to ask but are you confusing bounded
               | polymorphism with parametric polymorphism?
               | 
               | Bounded polymorphism is an application of parametric
               | polymorphism. Not sure what you are asking here. Possibly
               | this will clarify my line of thought: I consider
               | parametrically polymorphic code without any bounds to be
               | "uninteresting"/"trivial", because either the code must
               | treat the polymorphic type completely opaquely (leading
               | to trivial functorial [in the haskell sense] operations
               | like implementing fmap), or requiring you to explicitly
               | pass in all supported operations on the instantiated type
               | (basically requiring you to implement bounding by hand).
               | 
               | I think this may be a blub paradox thing, where the set
               | of useful applications of PP is much more restricted in
               | ocaml, to the point where ocamlers do not even consider
               | what they are missing. A haskell or rust programmer would
               | likely run into these semantic blocks quickly upon trying
               | ocaml.
        
               | octachron wrote:
               | Ok, you are using "parametric polymorphism" to mean
               | bounded polymorphism. This explain my confusion! Indeed,
               | I can understand your point of view then: bounded
               | polymorphism is indeed better done with functors in
               | OCaml. And since functor are syntactically heavy in OCaml
               | there are not used everywhere and only when they are
               | useful. Which doesn't mean that they are not used at all,
               | as the Mirage project can attest. MirageOS is essentially
               | an Operating System build upon OCaml functors.
        
               | wyager wrote:
               | I don't really consider those separate things. Bounded
               | polymorphism _is_ parametric polymorphism, plus a type
               | subset relationship. A quick sanity check on wikipedia is
               | consistent with this model. BP is PP plus what 's
               | required to make it more than a toy
               | 
               | I remember one annoyance I had with ocaml was the
               | effective inability to use point-free style. I can't
               | remember what limitation was behind this; do you know off
               | the top of your head?
               | 
               | Edit: it's the "value restriction", more annoying
               | bullshit I forgot about! Huge limitation for polymorphic
               | ocaml code.
        
         | substation13 wrote:
         | I think the issue is the compiler error messages being poor.
         | Automatic currying is wonderful for productivity (in my
         | experience).
         | 
         | Elm is not really an alternative to OCaml (or Rust) but it
         | shows how nice compiler error messages can be.
        
         | octachron wrote:
         | Do you have a more precise example in mind?
         | 
         | The example that you are describing should emit an error
         | message of the form                   This expression has type
         | type_of_arg_3 -> return_type         but an expression of
         | type return_type was expected
         | 
         | which seem alright to me. (But I cannot not be called an OCaml
         | aficionado). The type error might be delayed in sufficiently
         | polymorphism context but that is a more infrequent occurrence
         | (outside of functors whose error messages have been improved in
         | OCaml 4.13 partially for this reason).
        
           | muglug wrote:
           | yup, specific error message is here:
           | https://news.ycombinator.com/item?id=31861450
        
             | abathologist wrote:
             | Looks like that is exactly the error message predicted.
             | It's expecting a list of result values, but is getting a
             | list of functions from some stuff to that result.
             | 
             | IME, it's definitely true that OCaml doesn't have the
             | didactic error messages that many have come to like in Rust
             | et al. They said, imo they generally give the information
             | needed solve problems and I like their concision. It takes
             | a bit of time to learn to read them however.
        
               | muglug wrote:
               | > It takes a bit of time to learn to read [OCaml's error
               | messages]
               | 
               | Yeah, and I think you could say the same of Rust when it
               | comes to the borrow checker's messages.
               | 
               | But in Rust's case the problem it's trying to describe is
               | itself pretty complex. The complexity of OCaml's error
               | messages are really not justified by the problem they're
               | warning you about, which is itself very straight-forward.
        
               | ahmedalsudani wrote:
               | The ML languages started appearing in the 70s. Rust is a
               | very modern language.
               | 
               | Where do you think rust got (more than) half of its type
               | inspiration from?
               | 
               | Either way no one has to use any language. Personally
               | OCaml is not my cup of tea--Haskell is. But it is a great
               | tool and it's solving hard problems.
        
               | muglug wrote:
               | I used StandardML at university in the early 2000s, and I
               | appreciate that OCaml is an older language that heavily
               | influenced the design of Rust.
               | 
               | Given that history, it's instructive to note the sorts of
               | things (like auto-currying) that Rust did not inherit.
        
               | ahmedalsudani wrote:
               | Rust is a different language with a different audience.
               | 
               | If a functional language is presented today that does not
               | automatically curry calls, I have no interest.
               | 
               | Just because the tool does not work for you does not mean
               | there's a defect in the design.
        
               | octachron wrote:
               | I think that it is better to not forget that
               | expressiveness comes at the cost of a richer world of
               | misbehaving code. If you are used to a world where
               | functions have more than one argument and must be applied
               | to exactly the right number of arguments and nearly never
               | returns another function, starting a curried language
               | suddenly throws you in a situation where many small
               | mistakes like `plus 1` are not caught early anymore.
               | Moreover, those delayed mistake are here to allow room
               | for an expressiveness that you are not using yet. This is
               | a genuinely frustrating situation. And I do hope to
               | improve OCaml type error messages to better highlight
               | type difference at some point in the future.
        
             | zelphirkalt wrote:
             | Looks similar to what TypeScript will offer you.
        
         | munchler wrote:
         | FWIW, F# is very similar to OCaml, but its error message in
         | this case is usually quite clear. E.g.                   This
         | expression was expected to have type             'int'
         | but here has type             'string -> int'
        
           | muglug wrote:
           | yes, that would be a straightforward error message!
           | 
           | But I got the error message                   Error: This
           | expression has type              ((locl_ty * Tast.pos * ('ex,
           | 'fb, 'en) Aast.expr_) list ->               Result_set.t)
           | list            but an expression was expected of type
           | Result_set.t list            Type              (locl_ty *
           | Tast.pos * ('ex, 'fb, 'en) Aast.expr_) list ->
           | Result_set.t            is not compatible with type
           | Result_set.t
           | 
           | Which is not half as readable.
        
             | acchow wrote:
             | Stockholm syndrome arrives quite quick in this case, as you
             | become accustomed to this form as meaning "you're missing
             | an arg"
             | 
             | I agree the first 6 months of this is mind-numbingly
             | frustrating.
        
               | marcosdumay wrote:
               | This is not Stockholm syndrome. It's just learning a
               | pattern.
               | 
               | That's one of the top few specialties tasks for humans.
               | After you learn it, you immediately know its meaning, it
               | becomes easy with no thinking at all required.
               | 
               | Anyway, the plain English example from Elm on the sibling
               | comment is much better, because it's already easy to
               | understand before you learn to recognize it.
        
             | tgflynn wrote:
             | It doesn't look so bad to me. It's telling you that a list
             | type was expected but you passed it a function that returns
             | a list instead.
             | 
             | You're right though that OCaml isn't the kind of language
             | you can pick up in an afternoon.
        
               | whimsicalism wrote:
               | > you passed it a function that returns a list instead
               | 
               | I don't know OCaml really, but I would read that
               | intuitively as a list of (functions that return
               | Result_set.t), which is not compatible with a list of
               | Result_set.t.
        
               | imajoredinecon wrote:
               | Tiny quibble that I wouldn't normally post but I think
               | illustrates the readability issues here:
               | 
               | Should that be "Result set type was expected", not
               | "list"?
        
               | tgflynn wrote:
               | No, I think this: Result_set.t list
               | 
               | means a list whose items are of type Result_set.t.
               | 
               | Also I think the notation X.t is idiomatic for a type
               | defined by module X.
        
               | thingification wrote:
               | Can you explain (rather than state) what readability
               | issue you think exists here?
               | 
               | I don't know if you see a real readability issue or not,
               | but I do think it's easy to jump to "hard to read" when
               | that's only true in a certain parochial sense: it's not
               | what we're used to.
        
             | nh2 wrote:
             | For comparison, in Haskell you would get for this
             | definition:                   myfun :: Int -> Char -> Bool
             | myfun a b = undefined
             | 
             | if you give too many arguments:                   myfun 1
             | 'c' 3              * Couldn't match expected type 't0 -> t'
             | with actual type 'Bool'         * The function 'myfun' is
             | applied to three value arguments,             but its type
             | 'p10 -> Char -> Bool' has only two
             | 
             | if you give too few arguments:                   putStrLn
             | (myfun 1)              * No instance for (Show (p20 ->
             | Bool))             arising from a use of 'print'
             | (maybe you haven't applied a function to enough arguments?)
             | 
             | I like how it says                   applied to three value
             | arguments ... but its type ... has only two
             | 
             | and                   maybe you haven't applied a function
             | to enough arguments?
        
               | earth_walker wrote:
               | Elm's developers have been working hard to make better
               | error messages for these cases, and it really pays off
               | even when experienced. Here are Elm's versions for
               | comparison:                 > myfun 1 'c' 3            --
               | TOO MANY ARGS -------------------------------------------
               | --------------- REPL            The `myfun` function
               | expects 2 arguments, but it got 3 instead.            5|
               | myfun 1 'c' 3            ^^^^^            Are there any
               | missing commas? Or missing parentheses?            > not
               | (myfun 1)            -- TYPE MISMATCH -------------------
               | --------------------------------------- REPL
               | The 1st argument to `not` is not what I expect:
               | 5|    not (myfun 1)                  ^^^^^^^
               | This `myfun` call produces:                a -> Bool
               | But `not` needs the 1st argument to be:
               | Bool
               | 
               | Besides being 'beginner friendly', this just takes away
               | cognitive overhead when you can glance at an error
               | message and immediately know what happened.
               | 
               | (Edit: Formatting. Note that in the console output the
               | arrows actually line up with the source of the errors)
        
             | [deleted]
        
             | whimsicalism wrote:
             | This seems exactly the same to me just with more
             | complicated type names.
        
               | abathologist wrote:
               | Indeed it is :)
               | 
               | Dropping an example in TryOcaml[0]:                   let
               | f : int -> int = fun _ -> 1 ;;         val f : int -> int
               | = <fun>         let ex : int list = [f] ;;         Line
               | 1, characters 21-22:         Error: This expression has
               | type int -> int but an expression was expected of type
               | int
               | 
               | [0]: https://try.ocamlpro.com/
        
               | [deleted]
        
             | octachron wrote:
             | With OCaml type system permanently burned in my mind, I
             | parse that error message as "there is an ` _ ->
             | Result_set.t ` arrow type which is not compatible with type
             | `Result_set.t`". Do you think that the issue is that the
             | arrow `->` is too hard to spot?
        
               | muglug wrote:
               | Yes, I think that's the issue.
               | 
               | I built a reasonably popular typechecker for PHP, so I
               | have some experience with the DX for these sorts of
               | programs. I don't think it's good for a couple of ASCII
               | characters and line breaks to be the difference between
               | an acceptable and an unacceptable type annotation. It
               | requires, as you say, for the OCaml type system to be
               | burned into people's minds for them to immediately spot
               | the issue.
               | 
               | In a similar context Rust will just say "function
               | expected 5 arguments, you gave it 4" which has a very
               | obvious remediation.
        
               | thingification wrote:
               | I think it would be great to address this as you
               | describe, but at the same time as somebody who has
               | dabbled in OCaml but is still very wet behind the ears, I
               | don't understand how this error format requires the type
               | system to be "burned into your mind".
        
               | thingification wrote:
               | I have no insight into what is hard and what is easy to
               | implement here, but might it be feasible to break up the
               | error into parts, the first just as you wrote? Along the
               | lines of:
               | 
               | 1. Error: This expression has type ('x -> Result_set.t)
               | list but an expression was expected of type Result_set.t
               | list
               | 
               | 2. where 'x = (locl_ty * Tast.pos * ('ex, 'fb, 'en)
               | Aast.expr_) list
               | 
               | The solution suggested by the parent of your post sounds
               | desirable also, but perhaps the currying semantics of
               | Ocaml makes that difficult or poorly-defined?
        
               | octachron wrote:
               | Emphasizing the arrow (or in fact the most narrow error)
               | is definitively a good idea in that case. Counting the
               | number of arguments is in general a bit problematic: it
               | is possible to end with an accidental functional value
               | due to an extra argument send to a sufficiently
               | polymorphic function for instance.
        
               | ta-ocaml_typerr wrote:
        
         | int_19h wrote:
         | A big part of it is that currying is idiomatic, which, I think,
         | is a design mistake that most functional languages carry as a
         | historical baggage. But multi-argument functions can just as
         | well be represented as functions of tuples, even in OCaml (and,
         | if I remember correctly, it is idiomatic in SML), and then you
         | get a proper error message about the type of argument rather
         | than the result.
        
         | rwmj wrote:
         | We develop a large OCaml application and find the error
         | messages are fine. Changing signatures is an advantage when
         | refactoring because it identifies all the places you need to
         | make changes.
         | 
         | In addition I greatly prefer having a garbage collector around.
        
         | tgflynn wrote:
         | From what I've seen of Rust though it adds its own brand of
         | complexity due to its basic requirement for maximal efficiency
         | which precludes having a garbage collector.
         | 
         | I don't think I would want to use Rust unless I really needed a
         | language with near optimal performance. OCaml on the other hand
         | seems suitable for programs where correctness is important but
         | performance isn't the driving concern. At least I'm considering
         | using it for that type of application, though I'm not yet a
         | serious OCaml programmer (still working my way through this
         | course actually).
        
         | shadowgovt wrote:
         | This mirrors my experience in SML/NJ, where for the longest
         | time the most common error message the compiler would spit out
         | was 'tycon mismatch' and a Google search would not tell you
         | what a tycon was (it's a type constructor).
         | 
         | I think, unfortunately, I've observed a pattern in much
         | functional programming (F# being a blessed exception) that
         | relatively excellent computer language designers suffer from
         | the utter ineptitude of the human language competency of the
         | tool authors. These days, I am far more interested in an
         | elegant tool chain and support infrastructure than an elegant
         | language.
        
           | kopecs wrote:
           | Sadly I think SML/NJ has some of the worst messages for SML;
           | Poly/ML and MLton both end up with something more reasonable
           | than the dreaded                   Error: operator and
           | operand do not agree [tycon mismatch]
        
           | eckza wrote:
           | If you ever do any frontend work, you should give Elm a try.
           | The tooling is lightweight but effective; and the compiler
           | error messages are best-in-class.
        
         | kupopuffs wrote:
         | Forgive my ignorance, I'd would like an example of the
         | "functional" (hehe) benefits of automatic currying
        
           | earth_walker wrote:
           | Partial application and function composition are the meat-
           | and-potatoes of functional programming.
           | 
           | Automatic currying is syntactic sugar that can make these two
           | easier and clearer as it gets rid of a bunch of named
           | arguments and lambdas. For example, I can write:
           | foo a b c = a * b + c
           | 
           | I can then apply this partially like this (foo2 just takes
           | argument c):                 foo2 = foo 10 20
           | 
           | and compose it with other functions, for example like this:
           | composed = foo2 >> bar >> baz
           | 
           | Without currying, you'd have something like:
           | foo (a, b, c) = a * b + c             foo2 (z) = foo (10, 20,
           | z)            composed (a) = (\b -> foo2 (b)) ((\c -> bar
           | (c)) (baz (a)))
        
         | shikoba wrote:
         | > when signatures changed in an unstable dependency
         | 
         | The problem is not OCaml here.
        
           | muglug wrote:
           | I have a good point of contrast: I'm doing a similar task
           | with an unstable Rust dependency, and when APIs change the
           | error messages from the typechecker are crystal clear about
           | why things are incompatible.
        
             | shikoba wrote:
             | I see, you made the beginner mistake with OCaml. You should
             | never read the OCaml error message except if all other
             | options failed. Try to not read those messages, but just
             | look at the line where the error occured. You can use
             | -annot or -bin-annot when compiling and the Tuareg function
             | caml-types-show-type in Emacs will be your best friend.
        
               | c-cube wrote:
               | This is old advice :-). Use the LSP server and dune and
               | the error displays instantly, no further setup required.
        
               | shikoba wrote:
               | I don't get it. Is it sarcasm?
        
           | BiteCode_dev wrote:
           | Yes because we all work in perfect world under optimal
           | conditions.
        
       | 0x69420 wrote:
       | very grateful that this course is free for all, and the youtube
       | lectures are neat too
       | 
       | real world ocaml 2e is nice, but like a lot of oreilly books
       | about $LANGUAGE lately it's a lot of thinly-veiled $COMPANY
       | opinions on $LANGUAGE best practices, where $COMPANY is, in this
       | case, jane street. this is great if your motivation for learning
       | ocaml is applying for a job at jane street
       | 
       | if you think ocaml seems cool because wow jane street does epic
       | hft in ocaml, then read real world ocaml 2e
       | 
       | if you think ocaml seems cool because wow they wrote
       | coq/fstar/the early rust compiler in ocaml, then read cs3110
        
       | belmont_sup wrote:
       | Another day, another surprisingly large discussion around OCaml
       | whenever it comes up on hn.
        
       | omginternets wrote:
       | Forgive this tangential question, but does anyone know of a
       | practically-oriented tutorial for implementing an ML-style
       | language? Any format is fine, as are books.
        
       | hahnbee wrote:
       | Cornell alum here who took CS 3110. This course made me a better
       | programmer, made me gain a sense of respect for OCaml and
       | functional languages in general, and made me fall in love with
       | CS. The textbook and professor are both phenomenal.
        
       | frankohn wrote:
       | As a former OCaml hobbyist programmer my take on these kind of
       | books or articles is that, yes OCaml is extremely elegant and
       | beautiful as a programming language and it shines for simple
       | applications. Some parts of it are actually not so elegant, for
       | example the object oriented aspect completely spoil the elegance
       | of the core language.
       | 
       | On the other side OCaml, as a pure functional programming
       | language with immutable value by default doesn't scale well to
       | large, complex application. Just the paradigm is no longer
       | tenable and you need to switch at least partially to imperative
       | programming with mutable variable. For example this is what it
       | does the implementation of the OCaml itself.
       | 
       | To develop further the point about "what doesn't scale" there is
       | also the function with unnamed arguments and currying. While
       | extremely elegant for simple programs it gets confusing for real-
       | world applications when function needs quite a lot of arguments
       | and there is no longer any obvious order to give them. If you
       | stick with that and you choose an order it becomes arbitrary,
       | difficult to remember and currying no longer makes a lot of
       | sense.
       | 
       | Functional programming with immutable values is a wrong pattern
       | for programming languages. Many algorithms, almost all actually,
       | are naturally expressed in imperative style with mutable arrays
       | or variables.
       | 
       | What is needed is to bridge the good things from OCaml, the type
       | system, the pattern matching with tagged types into a modern,
       | imperative programming languages.
       | 
       | Rust is a sort of answer but they got it wrong because it is too
       | low level about managing the memory, the ownership pardon, and
       | everything else so programmers cannot just express the algorithm
       | or the logic they want to implement but they have to spend a lot
       | of mental energy thinking about ownership issues and unneeded
       | accidental complexity like lifetime annotations.
        
         | baby wrote:
         | Btw you can actually write loops and mutate things with the ref
         | keyword. It's usually much clearer than writing a loop
         | recursively but not everyone uses it.
         | 
         | The impossibility to return early in a function does create
         | really convoluted code though. I'm wondering if there's a
         | solution to that in FL
        
         | cdaringe wrote:
         | I'm with octaron on this one. No disrespect, but a bit of hand
         | waving and big claims.
         | 
         | I'm kindly invoking Hitchens razor, here
        
         | jimbokun wrote:
         | > Many algorithms, almost all actually, are naturally expressed
         | in imperative style with mutable arrays or variables.
         | 
         | https://www.goodreads.com/en/book/show/594288.Purely_Functio...
        
           | yuppiemephisto wrote:
           | Have you read the book and implemented its algorithms?
        
         | kn8 wrote:
         | > What is needed is to bridge the good things from OCaml, the
         | type system, the pattern matching with tagged types into a
         | modern, imperative programming languages.
         | 
         | Like https://rescript-lang.org/?
        
         | substation13 wrote:
         | > Functional programming with immutable values is a wrong
         | pattern for programming languages. Many algorithms, almost all
         | actually, are naturally expressed in imperative style with
         | mutable arrays or variables.
         | 
         | The optimal implementation might be imperative but that doesn't
         | mean we need to define our code that way. SQL is a good example
         | here.
        
           | agentultra wrote:
           | > The optimal implementation might be imperative
           | 
           | There are times when it isn't.
           | 
           | I'm thinking of Richard Bird's functional pearl, _The
           | Smallest Free Number_ , where the divide-and-conquer
           | algorithm is _faster_ than the imperative one.
           | 
           | From the conclusion:
           | 
           |  _One of the differences between a pure functional algorithm
           | designer and a procedural one is that the former does not
           | assume the existence of arrays with a constant-time update
           | operation, at least not without a certain amount of plumbing.
           | For a pure functional programmer, an update operation takes
           | logarithmic time in the size of the array.1 That explains why
           | there sometimes seems to be a logarithmic gap between the
           | best functional and procedural solutions to a problem. But
           | sometimes, as here, the gap vanishes on a closer inspection._
           | 
           | The ability to arrive at the divide-and-conquer algorithm is
           | quite fascinating as it uses plain, boring old mathematics
           | and is, for some, quite straight-forward to derive on one's
           | own.
           | 
           | Yet people are more convinced by imperative implementations.
           | I'm curious why this is. Is it because we "teach" people to
           | believe programs are executed sequentially and are therefore
           | somehow "inherently" imperative? Or is it easier to reason
           | about algorithms in terms of their operational semantics?
        
             | zasdffaa wrote:
             | > Yet people are more convinced by imperative
             | implementations
             | 
             | I like and use functional and immutable but saying that's
             | best is like saying a screwdriver is better. Sometimes
             | imperative is cleaner. In a video, the creator of Scala, M.
             | Odersky, said Scala allows mutability because sometimes it
             | is cleaner.
             | 
             | As an SQL guy, I remember one time a cursor solution was
             | cleanest and best.
             | 
             | Scala's librares have some string stuff which looks
             | immutable but isn't, under the hood.
             | 
             | As ever, it depends.
             | 
             | > Or is it easier to reason about algorithms in terms of
             | their operational semantics?
             | 
             | Good question. I'd say people think in terms of actions not
             | mathematical functions. I certainly do. Imagine teaching an
             | 8-year old. And it does map so well onto the underlying
             | reality of fundamentally mutable hardware.
        
               | agentultra wrote:
               | > And it does map so well onto the underlying reality of
               | fundamentally mutable hardware.
               | 
               | It maps well to the abstraction provided by the hardware.
               | In reality we know that modern architectures are
               | executing instructions out-of-order for efficiencies'
               | sake and that data-accesses are not necessarily
               | sequential either. And we haven't even considered
               | multiple-core processors that are so common these days!
               | 
               | As Bird points out in his book, when one looks close
               | enough there are cases where the logarithmic gap between
               | the imperative vs. functional approach disappears.
               | 
               | And further, with register targeting and stuff it's not
               | often true that recursive calls are less efficient than
               | their imperative counterparts anymore... although I
               | suppose historically this could be why, at least for
               | small arrays, imperative programmers could assume
               | constant-time indexing and updating.
               | 
               | I'm not nearly as versed in functional programming as I'd
               | like to be. I was raised on C and algorithms were always
               | described in terms of procedures to me where the proofs
               | took quite a bit of work to follow. However as I learn
               | more about algorithm design in functional programming and
               | functional data structures I can't help but notice that
               | the proofs are much easier to follow while sometimes the
               | solution seems quite alien and I wonder if that's because
               | of my heritage of thinking operationally rather than by
               | calculation. I've often been surprised to learn that many
               | of my assumptions about the logarithmic complexity of
               | functional algorithms are not always there!
        
             | substation13 wrote:
             | Ideally I would write my code in a declarative way and the
             | compiler would figure it out. Second to that, the language
             | can allow interior mutability so I can optimize.
        
             | throwaway17_17 wrote:
             | I tend to believe that there is an issue of map/territory
             | confusion in teaching 'programming' and in general thinking
             | about programming. Programs are usually envisioned as being
             | statements which are performed in a sequence one after the
             | other. This is comparable to the nature intuition about
             | what an algorithm is, i.e. a series of steps to achieve a
             | result when given some input.
             | 
             | The problem then lies in the deeper explanation. It is said
             | that the hardware is just taking instructions and executing
             | them one after another, and that is then mapped to the
             | execution of programming language statements. The issue of
             | 'imperative' vs 'non-imperative' implementations is really
             | a question of granularity. The map between machine
             | instructions and language constructs is so large (for most
             | every modern machine) that while the imperative algorithm
             | seems more reflective of the underlying architecture it is
             | just a layer of cover up to make programmers feel better.
             | 
             | I really think that the appearance of control over fine
             | grained instructional behavior give people the feeling that
             | their imperative code is truer/more-correct in relation to
             | the machine. This feeling leads to the presumption that
             | imperative is more efficient.
             | 
             | As a final caveat, the ability for a non-imperative
             | algorithm to be equivalent to its imperative alternative
             | tends to rely on the language's compiler (and the
             | restrictions inherent in the non-imperative language). This
             | is the origin of the 'with-a-smart-enough-compiler'
             | argument that people sometimes level against non-imperative
             | programming, i.e. there is not a compiler smart enough to
             | take your high level language and produce the same machine
             | code I do in my low level language.
             | 
             | So I'm sum, I think it is both a teaching error (the
             | continual re-enforcing of confusion between what a program
             | says vs what a machine does) and a general human level
             | confusion as to what an algorithm is at the level of
             | silicon in 2022.
        
         | int_19h wrote:
         | Ironically, I find the OOP part of OCaml to be the most
         | interesting, seeing how it manages to provide a self-consistent
         | and powerful structurally typed object model that is no less
         | powerful than what you get in C++, for example.
        
         | octachron wrote:
         | Concerning the issue with function arguments, this is one of
         | the reason why OCaml has labelled arguments. And for instance,
         | Janestreet's idiom udes labelled arguments as often as
         | possible.
         | 
         | Similarly, I am not sure what is the issue with using
         | imperative OCaml for imperative algorithms when they are a
         | better fit for the problem at hand?
        
           | frankohn wrote:
           | > And for instance, Janestreet's idiom udes labelled
           | arguments as often as possible.
           | 
           | So you have to give up to one of pillars of the functional
           | programming paradigm and you get a less elegant but more
           | practical programming language. Otherwise I agree that using
           | labeled arguments is mostly fine and doesn't completely spoil
           | the language.
           | 
           | The more serious compromise to the functional programming
           | paradigm is the fact that you need to use mutable variables
           | and imperative style programming. Once you do this you lose
           | most of the elegance and attractiveness of functional
           | programming.
           | 
           | > Similarly, I am not sure what is the issue with using
           | imperative OCaml for imperative algorithms when they are a
           | better fit for the problem at hand?
           | 
           | What I mean is that for _any_ moderately complex application
           | you need to switch to imperative style so the appeal of OCaml
           | is mostly lost. You better choose a programming language that
           | is designed for imperative programming since the beginning.
           | 
           | The arguments I am giving explains why there are practically
           | no real world applications done in OCaml. Some people insist
           | using OCaml because they love the elegance of the language
           | and I understand them but reality is it doesn't scale to
           | complex applications.
           | 
           | For people in Janestreet I think this is a sort of niche
           | where they get an added value from OCaml thanks to its
           | superior typing system and compile-time detection of many
           | errors. I guess they care really a _lot_ about the business
           | logic of their applications and OCaml shines to ensure it is
           | correct so for them the advantages out-weights the
           | inconveniences.
        
             | octachron wrote:
             | I am not sure which pillar of functional programming is
             | lost with labelled arguments? Partial applications still
             | work, higher-order function too. One might need to use
             | anonymous functions when labels does not match but I don't
             | see any pillar being lost.
             | 
             | In the same way, it is perfectly possible to switch to the
             | imperative style only in the specific code path where
             | performance really matters and use abstraction to isolate
             | this performance-sensitive part from the rest of your
             | application. Ideally, you can then keep both the elegance
             | of functional programming and the performance of imperative
             | programming.
        
               | [deleted]
        
             | [deleted]
        
         | throwaway894345 wrote:
         | I generally agree, and I'm hoping that Gleam fits the bill for
         | "Rust with garbage collector" (also, when I say this, people
         | leap to OCaml, but OCaml introduces a bunch of problems which
         | aren't present in Rust: cryptic syntax, lower quality build
         | tooling, unbounded type inference, competing "standard"
         | libraries, a fairly toxic community, etc).
        
           | kitd wrote:
           | > OCaml introduces a bunch of problems which aren't present
           | in Rust: cryptic syntax ...
           | 
           | Hmm, I think "cryptic syntax" is probably in the eye of the
           | beholder
        
             | throwaway894345 wrote:
             | I'm sure there's some element of subjectivity, but at a
             | minimum there's something to be said for "unfamiliar to the
             | overwhelming majority of programmers". I think it's also
             | very likely that OCaml's incredibly terse syntax (and terse
             | naming conventions) is _objectively_ difficult to
             | understand from a  "how the human visual/symbolic
             | processing pipeline works" perspective, but I don't have
             | the data to back that up (I also don't think OCaml is alone
             | in this regard).
        
               | int_19h wrote:
               | Thing is, if OCaml is terse and weird syntactically (and
               | I would agree), then so is Rust. Especially once you have
               | to spell out signatures.
        
         | zumu wrote:
         | > To develop further the point about "what doesn't scale" there
         | is also the function with unnamed arguments and currying.
         | 
         | Not sure what you mean by unnamed arguments, but criticizing
         | automatic currying is totally valid.
         | 
         | > Functional programming with immutable values is a wrong
         | pattern for programming languages. Many algorithms, almost all
         | actually, are naturally expressed in imperative style with
         | mutable arrays or variables.
         | 
         | This is a huge jump. Any imperative solution can just be
         | expressed as a fold of some sort and many algorithms, esp.
         | those that use stacks, are easily expressed recursively. Which
         | one is more "natural" is 100% subjective, but I'm in the
         | declarative is easier to reason about than imperative camp.
         | Moreover, the idea that "unnatural" algorithm expression
         | implies "doesn't scale" needs much more elaboration.
         | 
         | > What is needed is to bridge the good things from OCaml, the
         | type system, the pattern matching with tagged types into a
         | modern, imperative programming languages.
         | 
         | > Rust is a sort of answer but they got it wrong because it is
         | too low level about managing the memory, the ownership pardon,
         | and everything else so programmers cannot just express the
         | algorithm or the logic they want to implement but they have to
         | spend a lot of mental energy thinking about ownership issues
         | and unneeded accidental complexity like lifetime annotations.
         | 
         | It's not "wrong" just not what you want, and honestly the
         | ownership model isn't that bad. The overhead amortizes somewhat
         | as you get used to it. I of course agree there is room for more
         | languages with ML-like type systems though!
        
         | kaba0 wrote:
         | There are two ways to improve performance of a given program.
         | You either go lower level, giving more control over the
         | execution and specifying what you want exactly, or you go
         | _higher_ , not constraining the execution as much, allowing for
         | better optimizations.
         | 
         | For example a manual for loop will almost always be harder to
         | optimize than a map. The latter gives explicit permission for
         | reordering, allowing for vectorization and parallelization
         | automatically.
         | 
         | Also, I would think twice before claiming FP non-ideal for PLs
         | - modern CPUs employ just as much FPism as they are considered
         | imperative. OOE doesn't sound too imperative to me. And at the
         | end of the way, imperative steps are just sequential state
         | changes from a different perspective.
        
           | the_duke wrote:
           | > modern CPUs employ just as much FPism
           | 
           | Do you have some references where I could learn more about
           | that?
        
             | kaba0 wrote:
             | What I mean mostly are out-of-order-execution (reorder
             | these instructions and return results as if they were
             | executed in order - it is worthwhile because useful work
             | can be done "in the background" while the CPU waits for
             | memory. But this transformation is not too imperative in my
             | opinion) and SIMD instructions (and in extension GPUs) are
             | much more about transformations on data than a traditional
             | Turing-machine - but there are no sharp boundaries anywhere
             | here. It is just not smart to dismiss such a big and
             | important part of CS, when it has plenty of applications.
        
       | he0001 wrote:
       | How do I know that some code is beautiful? And how will one know
       | that it will always stay beautiful? Correct and efficient is one
       | thing. Beautiful? This is off topic but topics like this grinds
       | my gears as it's purely subjective.
        
       ___________________________________________________________________
       (page generated 2022-06-24 23:00 UTC)