Notice the "Why we like OCaml" page could have been written for C++
https://pbs.twimg.com/media/E9fNTDYWYAoxF17?format=jpg&name=...
Notice the "Why we like OCaml" page could have been written for C++
https://pbs.twimg.com/media/E9fNTDYWYAoxF17?format=jpg&name=...
I really wanted to know how Bloomberg uses OCaml since they are silver sponsors of OCaml [0], but it wasn't clear how they used the language in production anywhere, until right now in this post here.
But unfortunately, the opinions are mostly about X language vs Y rather than the post itself. It is ruined and that's not good at all.
But that is what they'd have to do with C++, since its problem is not the features it has, but that it has every feature.
Not yet, but there is continued enthusiasm for adding more features to C++.
Bjarne wants it to have lifetime tracking. Wouldn't it be nice if your C++ compiler could point out that you can't very well return this object since you've no reason to think it will exist by the time you return?
Vittorio wants it to have Rust-style language version management through epochs.
Herb wants it to offer slimmer exceptions instead of the bulky mess that people often opt to switch off entirely.
All of them agree C++ is too big. However, before it can be slimmed down properly, they just have one more thing to add...
Get ready for lambdas and better generic programming support.
They just keep forgetting to add safer types.
There was a proposal of a some lambda thing. From looking briefly over it, it seemed too complicated to me. I hope it doesn't get included.
If you look on the Wikipedia page for C2X, there is no mention of lambda: https://en.wikipedia.org/wiki/C2x .
Instead, pretty much all that is listed there are small and useful cleanups of the standard:
- two's complemement is required, which I assume will remove some UBs
- Labels can appear before declarations and at the end of compound statements
- Removal of K&R function definitions
- Unnamed parameters in function definitions
In fact those are some of the major complaints people are having with the language currently.As well as some very useful features that are small and self-contained
- A feature for binary resource inclusion in the preprocessor (#embed)
- Binary literals such as 0b10101010
- _Static_assert
I don't know the process and the people well enough to say I agree with everything WG 14 does, but I'll say that's how you improve quality of life while staying humble. The changes I see here, no chance of having to fix some grand idea over decades, like with RAII which requires N new type specifiers and constructors, move semantics...Wikipedia isn't always up to date, ISO mailings is where the stuff is.
http://www.open-std.org/jtc1/sc22/wg14/www/projects
http://www.open-std.org/jtc1/sc22/wg14/www/wg14_document_log...
Happy reading.
At the very bottom:
> Summary
> The C proposal N1451 provides a new syntax for lambda expressions. While details of the proposal are unclear, it is clear that there are both significant commonalities and significant differences [NOTE: document's title is "Comparing Lambda in C Proposal N1451 and C++ FCD N3092"]. The syntax is in many places a shallow inconsistency. More deeply, the model of reference capture seems inherently incompatible. The N1451 model seems more appropriate to Smalltalk, from whence it originated, than to C/C++. If the C commitee chooses to modify the proposal to be more inline with C++, then syntactic changes to both C and C++ would provide considerable value to working programmers, who often fail to appreciate any differences in approach.
In summary, there seems to be one guy who has invested effort in a proposal, and all I can find is at best mixed reactions to this proposal, inside and outside of WG14.
While the stuff I listed in above comment is all simple or even simplifying the language, and that's what's already integrated in the working draft in the standard.
That is how ISO works, one guy writes a paper and then has to champion it.
If you are in an hurry, you can use clang's block extensions, or GCC's version thereof (nested functions).
In any case, C23 isn't K&R C.
Okay, so they forgot the "isn't insanely complicated and abtruse" bullet point?
(EDIT: I don't mean to flame on C++. But slides notwithstanding, the difference between the two is pretty obvious.)
From anecdotal experience I view OCaml's bytecode compile speeds to be on par with Go's (as a rule of thumb I expect about 1 second per 10k lines). Far slower than something like TCC, but quite fast among other languages.
As for your view of OCaml byte code compilation speed being on par with Go seems rather unlikely or I'm misunderstanding what you mean. Go is not dealing with anywhere as near a complex type system as OCaml and generates a lot more potato machine code (usually not using -mnative so you can know you can copy a binary to arbitrary machines).
In general HM-based type systems are both conceptually quite simple and can have quite fast implementations. What makes them seem "complex" is that languages with HM-based type systems also tend to make it hard to "go around" the type system and enforce much more rigid discipline around how your code can be written.
I think this is due to the influence of Pascal and/or Modula on the language.
If you're asking about OCaml's pattern matching, it's compiled quite efficiently: https://www.cs.tufts.edu/comp/150FP/archive/luc-maranget/jun...
In practice it is actually very rare that they significantly affect compile times because it is very rare for the `n`-size to increase as the size of your codebase increases (these exponential blowups are generally limited to a single expression) for non-pathological code. Basically while you can have exponential blowups inside a single expression, I can't think of a time where you'll have exponential blowups across multiple expressions, which is what counts in a large codebase.
One of the times where big-O analysis doesn't accurately predict real-world runtimes.
To be clear, I think pattern matching is a good thing.
Go and HM-style type systems both have set operations they need to do that are of comparable practical computational complexity.
Exhaustivity checking without pattern matching (e.g. inserting wildcard expressions in every argument to a pattern other than the outermost tag) is computationally very straightforward and is at worst linear in the number of constructors a given type has. This is similar in computational complexity to Go's type checking of interfaces, which is also linear in the number of methods (since Go doesn't have any explicit interfaces it cannot simply do a name lookup).
// This still checks for exhaustivity and is at worst
// linear in the number of `Case0`, `Case1`, etc.
case thing of {
Case0 x -> ...
Case1 y -> ...
Case2 z -> ...
}
// This is how you get equivalent to SAT, but is also
// comparatively rare
case thing of {
Case0 True False True -> ...
Case1 True True True -> ...
Case0 True True False -> ...
// etc.
}
Exhaustivity checking in the presence of pattern matching is only non-linear in the number of parameters to the constructor, which in most code is only one (because beyond one usually you would use a record instead). So in practice there aren't any obvious computational edges that Go typechecking has.To add some data to the discussion, I have been recently measuring the compilation profile of some OCaml libraries, and the average time spent in the typechecker is around 40% (https://www.polychoron.fr/ocaml/2021/08/19/measuring_compila...).
Thus the complexity of the OCaml type system can only have a limited effect on OCaml compilation time.
And this measure does include interface files, where the compilation pipeline is reduced to just parsing, typechecking, and dumping the computed file to the disk.
The compiling pipeline of bytecode is basically typing -> lambda -> bytegen
Bytegen is really close to what Zinc does.
But doesn't the compiled code run slower in bytecode mode? I think that counts.
The healthy mix of interpreter/JIT/AOT backends is what many languages miss on their toolchains.
Making so that a project with 20k loc is less than a second in debug mode. And recompile time in native(debug) mode is almost 100% linking time.
For bytecode you can have wild rebuild times, in the milisecond range.
$ time make -C source -j8
make: Entering directory '/home/fnord123/src/download/pyre-check/source'
../scripts/setup.sh --configure
Build info: Linux x86_64 @ Mon Aug 23 2021 (development build)
abort: no repository found in '/home/fnord123/src/download/pyre-check/source' (.hg not found)!
Git commit: 1e4dd5a39db1631fd13a3a8df74d6e0f5d801fbc
dune build @install -j auto --profile dev
menhir parser/generator.{ml,mli}
Warning: 31 states have shift/reduce conflicts.
Warning: 5 states have reduce/reduce conflicts.
Warning: 77 shift/reduce conflicts were arbitrarily resolved.
Warning: 75 reduce/reduce conflicts were arbitrarily resolved.
make: Leaving directory '/home/fnord123/src/download/pyre-check/source'
real 0m43.700s
user 4m3.030s
sys 0m41.005s
I must be doing something wrong.[1] https://github.com/facebook/pyre-check [2]
$ tokei
-------------------------------------------------------------------------------
Language Files Lines Code Comments Blanks
-------------------------------------------------------------------------------
Autoconf 2 235 204 3 28
C 15 242641 165361 60669 16611
C Header 10 14267 2617 11094 556
Coq 10 2391 1968 220 203
CSS 5 713 540 44 129
Dockerfile 2 71 42 11 18
HTML 2 35 30 0 5
INI 1 12 9 0 3
Java 31 2654 2080 267 307
JavaScript 7 867 752 61 54
JSON 17 939 939 0 0
Makefile 5 229 145 45 39
Markdown 47 7213 7213 0 0
OCaml 791 229096 198302 10455 20339
Python 398 61909 50119 2917 8873
Shell 5 111 59 34 18
SVG 2 2 2 0 0
TeX 4 929 642 129 158
Plain Text 5 457 457 0 0
TOML 1 2 2 0 0
TypeScript 2 130 84 20 26
-------------------------------------------------------------------------------
Total 1362 564903 431567 85969 47367
-------------------------------------------------------------------------------Reading your table, there are 198302 lines of OCaml code. That's about 10x larger than the 20k you claim. The 20339 in that row is the count of blank lines. I doubt they matter much here.
template<auto f> struct my_type{};
using t1 = my_type<123>;
constexpr t1 val1;
using t2 = my_type<val1>;
?Because vanilla OCaml doesn't have any mechanism for expressing compile-time computations, the same level of transformations aren't possible, but I wouldn't call this a limitation of the type-system, rather the meta-programming capabilities.
Although, you can somewhat approximate it with modules and functors:
module type M = sig type t val t: t end
let my_type (type a) (v: a) : (module M) =
let module M = struct
type t = a
let t = v
end in
(module M : M)
module T1 = (val my_type 123)
type t1 = T1.t
let val1 : t1 = T1.t
module T2 = (val my_type val1)
type t2 = T2.tBut this is literally what a type system is about ? TS definitions don't care about compile-time and run-time ; what you call "macros" are type-level functions which are a byproduct of the expressivity of C++'s TS.
Remember that expressivity of a type system is just the cardinality of the type set you can express with it.
Type systems where you can parametrize on nothing are less expressive than type systems that can parametrize on types, which are less expressive than TS which can parametrize on meta-types, which are less expressive than TS which can parametrize on meta-types and integers (C++98, Haskell afaik), which are less expressive than TS which can parametrize on meta-types and arbitrary compile-time values (C++20, soon Rust I believe ?), which I'd guess are less expressive than full-blown dependent types but I'd need to check the exact definition
Haskell also supports type-level strings and type errors, as well as arbitrary ADTs if defined with the -XDataKinds extension enabled. Not sure what you mean by ‘parametrizing on meta-types’ though.
When we are talking about a general-purpose language like OCaml, then you come in talking about extreme HPC–you must realize it's not relevant in the discussion? Would you comment on threads about Golang talking about how it's not appropriate for HPC usecases?
But when talking about performance the point is to extract the absolute most performance possible of a given hardware, because you are building a product and you can only afford a.g. some ARM chip and need to get the most out of it to make your product's price fit for its target demographic (to, you know, make money for your business).
In particular, the main "competition" in that field is analog hardware which does not have "performance" issues (but others instead : an analog guitar distortion does not have latency but it does raise the noise floor in the signal) - everyone wants the best of both worlds and it is our job to make it happen
For example, many popular game engines provide C# scripting engines _even though_ using a garbage collected language (even just for scripting) is slow (and throwing away % of performance) because the performance is _good enough_ for their usecases.
> execution speed does not matter
What I actually said: what is the actual performance requirement?
I don’t get why this is such a hard concept for people in this thread :-) Obviously you should use the right tool for the job! If you have extreme HPC requirements, no one (except maybe Jane Street) is going to use OCaml for that. It’s a high-level GC language! When you come into a thread, have some basic context about what it’s about.
https://ocamlverse.github.io/content/metaprogramming.html
In C++ the system for generics is the same system for metaprogramming. In OCaml they are two different systems.