Search
 
   

935 results for "ocaml"

  1. 2

    OCaml’s multi-core needs to happen. Even then there’s not an exact overlap with Rust due to GC (and multi-core means OCaml’s GC won’t be as fast anymore). Add in the “weird syntax” and OCaml is handicapped out of the gate.

  2. 4

    This article is very Silicon Valley, by which I mean the confidence in its assertions outpaces the depth of knowledge.

    I’m surprised that nothing was said about runtime performance. That was, historically, a reason for using statically-typed languages and I think that it applies today. Dynamically typed languages, when used idiomatically, are slow. Yes, Clojure and Python can be fast, but typically this involves writing core libraries in other languages (Java or C) and writing code that isn’t idiomatic. That’s not a deal-breaker because (a) for many applications, reasonable performance is good enough and dynamic languages are fine, and (b) high-performance code (while not that hard to write) isn’t idiomatic in Haskell either.

    Next, most modern languages are strongly typed (static vs. dynamic is not the same thing as weak vs. strong). This includes Python and Clojure. They are dynamically typed languages with strong typing, enforced at runtime (hence “dynamic”). Weak typing is when you can cast a double to an int64 or void * to int * and it’s legal. C is weakly typed by design and has to be that way (malloc shouldn’t care what you’re using the block of memory, starting at the void *’s value, for). So you can get correctness checking in dynamic languages. The difference is that it doesn’t happen automatically. You either need runtime assertions (at a cost to performance) or an inclusive test harness. Given that most corporate code has behind it the bare minimum of effort necessary to get it to work, those tools are rarely used to even a fraction of their capacity. And the people who would be most insistent about using them tend to prefer static typing when possible, because we recognize that we’re imperfect.

    “Dynamic vs. static” is about whether type errors are caught at compilation or during runtime. And this would be a settled question (it’s always better to find errors earlier) except for the fact that static typing requires a compromise (in my opinion, a slight compromise that is often negligible in comparison to the amount of debugging time that it saves) to the programmer in terms of how easy it is to write programs.

    The issue around static typing is that there are a variety of type systems, each with different strengths. OCaml has fast compiles but its type system isn’t as rich as Haskell’s and it doesn’t have (as of my knowledge, which may be a few years out of date) the same convenience that you get from type classes. You have to use functors to get that functionality. Haskell has a great type system overall but compiles can be slow and laziness isn’t universally beloved. Scala is a mess but it interoperates with the JVM. Languages like Idris allow dependent types and bring more power into the type system, but the best way to do this is still an area of active research (for example, it’s possible, with dependent types, to make type checking undecidable; however, this is also possible with Scala, so it’s not necessarily a deal-breaker).

    We’re now at a point where the productivity cost of static typing is generally quite low. There are usage patterns that require a bit of wizardry (try reinventing printf in idiomatic OCaml) but they’re uncommon and usually indicative of a design flaw. Best possible performance is better in statically-typed languages than dynamic ones, but still hasn’t beaten optimized C. What level of compile-time type discipline is “the best” is subjective and so is how to achieve that (e.g. Haskell’s approach vs. OCaml’s) and still a matter of debate and research.

    I do think that dynamic languages have a place in the world. For one thing, there’s a conceptual beauty to Lisps (computation over language at a user level, rather than wizardry done by the compiler) that is worthy of study. Secondly, highly-interactive work like exploratory data science often benefits from being done in a dynamic world (data-dependent manipulations on R-style data frames are a pain to statically type).

  3. 3

    I love seeing an ML on BEAM. I wish the author decided to port an existing ML over to BEAM rather than make a whole new language. With Ocaml, they could have gone the jsofocaml route which converts ocaml byte code into JS. So ocaml byte code to BEAM byte code could have been quite nice.

  4. 11

    What’s gained by using a new term “compile-time computing”? These techniques are well-established, and I feel like there are already too many terms.

    I think metaprogramming is a good umbrella term which encompasses a variety of techniques. It’s more general than “compile-time computing”, and I think gets to the heart of the issue.

    Metaprogramming is when the data a program operates on is code – whether in textual form, AST form, or “runtime form”. Not coincidentally, that’s also a good definition for a “compiler” or “interpreter”. Examples:

    • code generators are metaprogramming – the code is is text (e.g. lex/yacc/protobuf compiler)
    • macros are metaprogramming – the code is in the form of ASTs
    • runtime reflection is metaprogramming – e.g. dynamically creating classes in Python, Go’s JSON module, Java class loading
    • multi-stage programming [1] is metaprogramming – this relates to whether the generated code is typesafe. OCaml has a few systems like this.

    The problem with “compile-time computing” is that the same DSL can be implemented in a variety of ways. Whether it happens at compile time or runtime is orthogonal to what you’re doing. That you’re using metaprogramming is a more apt description than “compile-time computing”.

    Examples:

    • lexer / state machine generators

      • Rust and OCaml have compile-time macros to implement regexes
      • lex, re2c, ragel – done at compile-time with textual code generation
      • PCRE / re2 – regex interpreters, as opposed to compilers
      • v8 apparently has a regex JIT
      • in Lisp, you can do these at compile time, but with macros as opposed to textual code generation
    • parser generators

      • ANTLR, Bison – done at compile time, with code generation
      • C++ has parser generators done entirely in template metaprogramming, e.g. boost::spirit I think
      • there are several parser “interpreters”, e.g. I think many parser combinator libraries. I wrote a PEG parser interpreter in Python. PyParsing is another parser-interpreter I just looked at.
      • in Lisp, you can do these at compile time, but with macros as opposed to textual code generation
    • AST schema language, like ASDL [2]

      • Python’s ASDL – Python uses a Python program which turns an ASDL schema into a C library.
      • Oil’s ASDL – but I currently use an interpreted variant of this Oil. There is no textual code generation, it’s done with runtime reflection.
    • schema compilers / message formats (like protobuf)

    • web template languages – You can find more than a dozen such languages in Python, Ruby, Perl, PHP, etc.

      • most of these are AST interpreters
      • Cheetah was compiled to Python bytecode IIRC
      • There are multiple template languages that can be compiled to C and C++; I used one at Google and there were others too.
    • printf format strings – These are just like web template strings; in fact printf is accidentally Turing-complete [3]

      • The C stdlib has interpreters for printf strings (however modern C compilers have additional parsers for them to give compile time error messages.)
      • OCaml (and maybe Rust?) have printf string compilers

    My claim: for any example he gives of “compile-time computing”, you can find an equivalent example of metaprogramming that is not done at compile-time. The fact that that it’s metaprogramming is the interesting thing about the software architecture.

    Compile time and runtime don’t really have solid meanings anyway – Lisp intermingles the two. There is a Cling interpreter for C++. When you’re writing compilers and build systems, build time is runtime! (That is not a vacuous or trivial statement, if you work on that type of thing it comes up)

    EDIT: I feel like the Futarama projection proves my point, i.e. it’s irrelevant whether it’s a compiler or interpreter, but I probably won’t do a good job explaining that: https://en.wikipedia.org/wiki/Partial_evaluation

    [1] https://en.wikipedia.org/wiki/Multi-stage_programming

    [2] http://www.oilshell.org/blog/2016/12/11.html

    [3] https://github.com/HexHive/printbf

  5. 4

    Getting started with OCaml is so much easier nowadays than it was just a couple of years ago.

    Here’s a quick guide for anyone who wants to try the modern development workflow in OCaml: https://medium.com/@bobbypriambodo/starting-an-ocaml-app-project-using-dune-d4f74e291de8

  6. 7

    Interesting writeup. I’ve been using functional languages for many years at work, and over the last 15 or so years I did the round trip from SML through Haskell and recently back to the ML family with OCaml. The jump from SML to Haskell was largely due to the flurry of activity in the Haskell community that seemed to increase about, oh, 5-6 years ago - around the time of RWH and a number of good articles and presentations demonstrating recent developments in the compilers (such as the parallelism libraries). My move to increase the amount of OCaml in my workload is mostly due to the ability to clearly reason about space/time performance of code. A consequence of laziness in Haskell is that it is non-trivial to reason about space and time requirements, as well as how to tune for them via strictness annotations or the use of strict versions of data structures or library implementations. After working through the process a few times, I found it refreshing to return to the ML family and find that I could reason about time/space in my code. I’ll probably split my projects between Haskell and OCaml from here on out - they both have their place, and the both have their limitations/pain-points.

  7. 2

    I just bought a copy and it’s copyrighted in 1998, transferred to digital format in 2003. I don’t think there are newer editions of it.

    Also of note: SML looks very strange to me, I kind of wish it was in Haskell or OCaml just for the familiarity factor. His SML code looks nothing like the OCaml that I know (though I’m coming from F#, not OCaml).

  8. 3

    But this approach would be difficult to reconcile with the goals of Go. Go is positioned as a systems programming language (to program application servers, not operating systems). Using boxing almost everywhere is not an option here.

    Why? Ocaml is used as a systems programming language as well, boxing has not been an issue. Java is one of the most used server side languages and it boxes everything. I think this statement has zero evidence to back it up.

    This is the reason why interfaces are not the default mechanism for everything. This is also the reason why generics are a little bit more difficult to implement than in OCaml.

    If I need to do anything generic-like in Go, I have to use some kind of interface, the worst case being interface{}. Therefore: I already have to drop to interfaces to do generic things, so I why can’t I implement generics as a compile-time construct that produces interface{} code? I’m not saying you should, I’m saying as a thought experiment these things are semantically equivalent. You only need to drop to a boxed value when you’re doing something generic, and the compiler knows when you’re doing something generic, so it can produce code equivalent to what a human would write, but proven correct.

    Note: this does not restrict you from writing a container that is specific to your type (which is the current state of Go) but it provides the ability to provide generic data structures at a cost of some pointer indirection. So you’re in no worse a state than today. And for many problems the extra indirection does not matter. See Rule 1.

    The runtime cost is related to pointer indirections and having to allocate/free memory on the heap for each value.

    Yes, as I said Ocaml provides tools to remove the indirection in performance critical places. BigArray was explicitly made for this. And record types have some optimizations on this that one can make use of.

  9. 17

    The argument here is really:

    Java or Python? Use Java because fast.

    I found the argument to be poor. Maybe if Java is the only alternative to Python you’re aware of it sound pleasant but otherwise it sounds like a nightmare. I have Ocaml, a language that is significantly more enjoyable to write than Java, nearly just as fast under many workloads, and a runtime that is orders of magnitude simpler such that I can actually understand it. Why would I use Java over that without getting my arm twisted. On top of that, Java completely sucks for CLI tools (like the one mentioned in this story) where as Ocaml is great for them. For other people, Ocaml fills a similar niche that Go does.

    I think story the author really wants to tell, and hinted at it a few times but didn’t finish the thought, is that one should prefer to minimize their technology choices and stray from them only when it is required. So if your codebase is Java, stick to Java for most things unless you have a great reason not to. If it’s in something else, then stick to that. But the lesson is to minimize complexity by minimizing tools. Java itself still sucks and the only reason to continue with it is momentum.

  10. 1

    Although Haskell is pretty great, it’s a bit of a mountain at first. I would start with either OCaml or Erlang, both of which are a little easier to get into.

    I can’t wait to show this to my coauthor whose first and only programming language is Haskell.

    Learning OCaml and Erlang will not teach you Haskell. There’s little overlap and you won’t obtain the same benefits.

    Haskell is learnable for FP experts, people completely new to programming (like my coauthor on our book), and everyone in between.

    OCaml and Erlang are worth learning for their respective strengths alone. They’re not a waiting room for Haskell and learning them first won’t save you any time.

  11. 7

    One way of rephrasing it might be: the ML community outside OCaml is stagnant. There used to be a number of “branches” of the ML community with active projects, but nowadays I think SML/NJ is the only other one even making regular releases, and their releases are mostly maintenance-oriented.

    I’m not too sure if everyone in ML-land simply coalesced around OCaml, or if the people who were members of other parts of the community went elsewhere. I recall that at least when I last had much contact with ML (~10 years ago), many of the SML people weren’t big fans of OCaml, so I would be at least slightly surprised if they ended up there. But some may have. Also, I think many of the PLs researchers, who used to be a biggish part of the ML community, have moved to Haskell (with some exceptions, like some of the folks at CMU).

  12. 4

    One thing that’s interesting to me about this is the possibility of using OCaml for more serious network services work. That’s not to say that OCaml isn’t used for serious work already, just that something like Go, with builtin multicore and concurrency support is more likely to be adopted at this point in time.

    So, anyway, with OCaml being a great language to begin with, having loads of benefits, and a (seemingly) growing community, how will it compete with Rust / Go? Can it compete with Rust and Go?

  13. 4

    Rust has its sights on targets that are ill-suited to OCaml or Go.

    Agreed.

    With network based services (the target of my comment, my bad if it wasn’t clear), I/O is typically the biggest bottle neck, so, fairly efficient machine code compilation (which both OCaml, and Go have), should put them in the same “ballpark”, I’d guess for things like web applications and other similar workloads. This of course assumes a similar I/O model, which I believe can be the case. Rust uses libuv, right?

    I’m just wondering if we’ll see an increasing number of “How my startup went from 50 Node.js servers to 2 OCaml Servers” anytime soon, cause if so, sign me up for hopping on the bandwagon first! :-)

  14. 13

    An extremely innovative programming language, only understood by a few programmers, and very difficult to learn, would be useless to the industry.

    I agree that we need innovation in programming languages, but I also think that a new programming language must fit in a “complexity budget”. It’s okay for a programming language to innovate a lot, if it fits in this “complexity budget”, which makes the language learnable in a short amount of time.

    A language learnable in a few weeks can be used by almost any programmer. A language learnable in a few months will exclude programmers that are unable or unwilling to dedicate evenings or week-ends to this learning. A language learnable in a few years would require a sabbatical (but I’m unaware of any existing programming language requiring such an investment).

    My point is innovation in programming languages too often comes with an increase in complexity budget. And the more we increase the complexity budget, the more we limit the audience.

    Rob Pike wrote “The need to get programmers productive quickly in a new language means that the language cannot be too radical”. I would add that a new language can be radical only if the increased learning time is compensated by an increased productivity and quality in the future.

    By the way, if you compare Go to mainstream languages like C/C++/Java/PHP/Perl/Python/Ruby/JavaScript, it’s not that uninteresting. It brings thinks like CSP concurrency, structural typing with interfaces, composition over inheritance with embedded structs. Of course, if you compare it to OCaml/Haskell/Rust/Scala/F#, there is no question that Go is less innovative and radical (as a language – I’m not talking about the runtime, the tooling and the library). But the truth is that OCaml/Haskell/Rust/Scala/F# have not been designed to be mainstream languages. Go was.

    Maybe what you/we need is a successor to OCaml or Haskell, but with the design goal of becoming mainstream, at every level of the language (language itself, compiler, runtime, tools, libraries, ecosystem, etc.). What would be the equivalent of Go at Google: Language Design in the Service of Software Engineering for this new language?

    PS: One example of a programming language that I found quite radical and innovative for its time, and still easy to learn, would be Erlang.

  15. 2

    having shifted a lot of my hobby programming from ruby to ocaml, the op’s description of type system rigidity paradoxically offering you greater freedom to explore is spot on. ocaml is a joy to work with because i can alter some fundamental data structure, make the associated sweeping changes across the code, and have everything magically just keep working once the compiler is happy.

    then again, i migrated at least one project from ocaml to ruby just because OpenStruct made the code so much shorter and cleaner, so dynamic languages have something to be said for them too :)

  16. 17

    One thing missing form this is that “static types” is a huge range. Does one mean Go? Java? Ocaml? Agda? Agda and Go are farther apart than Python and Go. The type system Go gives a developer is roughly equivalent to the mental model a Python developer has when writing Python. In that sense, the Python developer was already using static types, Go just lets them write them down. I’m a static type enthusiast, and while I’d rather write Go than Python, it’s not by much. I’d much rather write Ocaml.

    From a type theoretic point, you can always encode a dynamically typed language in a statically typed language. From that perspective, static types have always been winning, language authors have just chosen to not finish implementing their type system. And the nimblness that a dynamic language is giving someone is disappearing. Haskell has type holes, so you can write your program and run it for the cases that you had it make sense and then let the compiler help you finish it.

    For myself, I’ve replaced most places that I would use something like Python with awk and shell scripts. Anything else my goto is Ocaml. At work, I generally have to use something like Python or Java, but not for any particulaly good technical reason, just some politician in engineering won that argument that day and those are the tools we can use.

  17. 3

    To defend Ocaml, my language of choice, it has a great property where the translation between the Ocaml code and the machine code is fairly understandable, meaning you can understand what your program is doing at runtime.

    This might change a bit with the compiler beginning to pick up some optimizations, like in the flambda branch. Overall I think OCaml will continue to exhibit an easier to understand runtime behaviour compared to Haskell.

  18. 3

    Last I looked, Java, Ocaml, and Go were all about equal on average. Note that implementations of Ocaml and Go both have received orders of magnitude less attention than Java. But I haven’t checked it too much. I also don’t know if the Java, Ocaml, and Go code do unsafe things to get their speed.

    Is it possible to get the same level of cross-platform consistency that the JVM gives you (e.g. memory model) from an AOT language?

    You would have to be compiled per target, but there is no reason you can’t get those guarantees. Java has to provide them somehow, right? And that isn’t what the JIT is doing, the JIT is just implementing the on-demand compilation.

    It depends on usecase, but IME, we pretty much only compile software for one system. Pretty much any Java backend deployment does not need 90% of the platform independence that is baked into the runtime.

    I agree that there’s not a compelling performance advantage for JIT, but I don’t see that that means it should be abandoned

    I think JIT is a valid path of research, I’m never against research, I just have not found it beneficial in production systems. It is a huge complexity, at least in the main languages used (LuaJIT is not so bad, AFAIK, but it is used at a much smaller scale). Its complexity that I do not want in a production system because it’s effectively just dead code in the environment that gets in the way of knowing what is going on. That is the compelling argument for AOT, IMO: simplicity. Do less things, give me just want I need. But, YMMV.

  19. 4

    OCaml with Core as your stdlib is actually pretty solid. All methods that return exceptions are explicitly marked by ending in _exn, and I can’t think of any example where there isn’t an equivalent version that instead returns Some foo or None, which (unlike in Go) you cannot ignore and must pattern match sanely. This post and its ancestors provides a good comparison of Python and OCaml from the perspective of moving a very large code base from one language to the other incrementally.

  20. 1

    I have never heard of a compiler that type checks while it is parsing, or even builds up a type context while its parsing, and so, I still do not get the distinction between “this whole time”.

    Both sets of languages do this in a separate step. Both sets of languages have rules which they apply. Both sets of languages have an algorithm for applying said rules (as you pointed out OCaml has type unification, where as C++ does not). Both sets of languages provide some level of proof that the program is coreect, from a type standpoint (with OCaml obviously going much farther here).

    The only difference that you’ve pointed out here, is simply that OCaml uses unification, which is just part of the algorithm for applying the rules. Yes, the algorithms are different, but that doesn’t make one less inferency than the other.