![](/i/fill.gif) |
![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
So today I started learning (or at least familiarising myself with)
Ocaml (Objective CAML). But this isn't going to well; I find myself
continually screaming at the computer screen "YOU'RE DOING IT WRONG!"
If you don't care about why Ocaml is wrong and why Haskell is better,
you can stop reading now.
As best as I can determine, Ocaml is some sort of hybrid of Java and
Haskell. It's "mostly functional" and has syntax resembling Haskell, but
it also has object-oriented features.
Personally I prefer the 100% pure approach of Haskell. But hey, maybe
sometimes it's better to be pragmentic and go with a hybrid approach. It
could be interesting to at least see how that works out, right?
I get the impression Ocaml is from academia too (just like Haskell), but
it's designed especially with a focus on performance. This alone
probably explains why it's strict (like C, Java, VB, C#, Pascal...)
rather than lazy (like Haskell). Making Ocaml strict rules out all sorts
of possibilities, and gives lower performance in some cases, but it
makes performance *predictable*. And that's probably the Big Reason for
this design choice. [It also makes several implementation details much
simpler.]
Problems become apparent almost immediately. I had a look at the Great
Language Shootout, where I found Ocaml getting royally OWNED by both C
and C++, mainly because both of these used all four CPU cores, while
Ocaml apparently can't. Ocaml supports multiple light-weight threads,
but because the garbage collector isn't thread-safe, all Ocaml threads
run in a single OS thread - i.e., on a single CPU core.
Haskell, of course, runs your threads on several cores, like you'd
expect. (Unless you specifically ask it not to because you're trying to
interface to external C libraries that use thread-local storage.)
Of course, even Haskell currently has a parallel but not concurrent GC,
and in particular to perform a GC cycle you have to wait for all Haskell
threads to stop. (They can only be stopped at certain points.) This can
cause large program slowdowns, where one thread is in a tight loop and
won't stop, and all the other threads are paused waiting to start a GC
cycle.
But hey, all of this isn't really to do with *Haskell*, but rather with
*implementation*. Specifically, I'm talking about GHC. Other
implementations do exist. [Most of them don't support SMP *at all*
though.] You could write another implementation, or fix GHC, and the
above drawbacks would go away. I have no idea how many Ocaml
implementations there are, but it seems plausible you could also make it
to real multithreading too.
Then I started to look at the syntax. It's *mostly* identical to
Haskell, but with a few differences. Most of the differences are just
/different/. Haskell uses one symbol, Ocaml uses a different one. No big
deal. But a few of them are quite nausiating.
In Haskell, I can write
foo = 5
In Ocaml, you must write
let foo = 5
Small bit of superfluous chatter there. Still, I guess it makes the
syntax more consistent. Slightly. But then we get to the next
abomination: In Haskell, I can say
foo = bar foo
but in Ocaml, you must say
let rec foo = bar foo
Yes, that's correct, you have to manually tell the compiler that you're
making a recursive definition. WTF? What, it isn't trivial enough for
the compiler to detect this all by itself? (You might try to argue that
it prevents you writing recursive definitions when you didn't mean to,
but this is *functional programming*! Recursion is ubiquitous!)
Another puzzling thing is that Ocaml appears to use a layout rule
similar to Haskell [not that the tutorial I'm following bothered to
point this out], and yet it still requires explicit semicolons anyway.
(??) And there's a set of fairly complex rules for when you must and
must not write them. (?!?)
Now in Haskell - or in fact any vaguely modern programming language - if
I want to add two numbers together, I say
x + y
BASIC had this, Pascal had this, C had this, Java had this, Smalltalk
had this, and Haskell certainly has this. But not Ocaml, apparently.
Here you must use "+" for adding integers, "+." for adding reals, and
"+/" for adding arbitrary-precision integers. (And presumably some other
symbol for rational numbers, complex numbers, vectors...)
This seems pretty much inexcusible to me. I'm sure there's a technical
reason for why they did it this way, but it seems like a very basic
thing to get wrong. Some languages have overloading, some languages have
classes, and some languages just have interfaces, but all of them seem
to manage to avoid this. As far as I can tell, the above makes it pretty
much impossible to write code that works on more than one number type.
In a similar vein, in Haskell "7" is a number. Any type of number. It
can be anything. But in Ocaml, "7" is an integer, "7." is a real, and I
forget what the syntax for an arbitrary-precision integer is.
In Haskell, you can say "Set Integer" or "Set String" or "Tree Integer"
or whatever. Apparently in Ocaml you have to say this backwards:
"integer tree" and so forth. I haven't yet seen any higher-kinded type
constructors, but it'll be interesting to see how that works!
Haskell uses the convention that type NAMES must begin with a capital
letter, while type VARIABLE must begin with a lowercase letter. Ocaml
uses the arguably superior convention that type variables start with an
apostrophy. (I would have used a different symbol, but it's a reasonably
design choice. Somebody recently pointed out that some Unicode alphabets
have no concept of "uppercase" and "lowercase".)
Which reminds me - in Haskell, "Char" is a 32-bit Unicode character.
Ocaml makes the mistake of using an 8-bit ASCII code. (But apparently
there are libraries to "work around the problem".)
Haskell has a thing called "tuples". A tuple is a fixed-length,
fixed-type collection. For example, you might write a parser function
that takes a string and returns (say) an integer and the remainder of
the string. To do that, you return a 2-tuple containing an integer and a
string.
The literal value of such a tuple can be written as
(5, "rest of string")
and its type is written as
(Integer, String)
Ocaml has the same syntax for writing tuples, but the type signature becomes
integer * string
which seems a rather perverse choice. I can see what they're getting it
(a tuple type is a product type, whereas an enum is a sum type), but
that's still rather far-out.
(Haskell also uses "[Integer]" for a list of integers, and "[3,5,7]" for
a value of this type. In general, the type signatures mirror the value
syntax - possibly to the point of being confusing. Personally, I've
never been that fond of the list and tuple syntax. Sure, lists and
tuples are common, but would it really kill you to write "Tuple2 True 5"
instead of "(True, 5)"? It would be clearer...)
Also, Haskell's "()" type becomes "unit". Which is the name Haskell
programmers use to refer to it, but hey. It's not entirely clear to me
whether Ocaml lets values of type "unit" actually exist - and if so,
what their syntax is.
Haskell has algebraic data types. Every Haskell type is nominally an
ADT. (Although in the case of "Int32", it's an enumeration type with
constructors named "0", "1", "2"...) All types follow the same uniform
structure.
Ocaml, on the other hand, has "records" which are like "{field1=5,
field2=true, field3="hi"}", and "variants" which are like Haskell ADTs.
And they're apparently not the same thing. You can see this when you
define one:
type recordX = {field1 : int, field2 : string, field3 : stuff}
type variantY = Thing of int * string * stuff | OtherThing of char * char
So "records" use named fields, but have only a single (unamed)
constructor, while "variants" allow multiple constructors, but use a
wierd syntax for defining the fields - a syntax resembling tuple types,
actually. WTF?
(This quite apart from the parameter of a parameterised variant being
written *before* the variant name, not after it. I wonder if you can
have multiple parameters on a variant? I haven't seen any examples
showing this. I also haven't seem any parameterised records, so I'm not
sure if that's supported. I'm also not sure how it knows from looking at
a record which type you mean...)
Haskell makes the rather illogical choice of using "--" as the start
marker for a comment. (Great. So I can't use that as a name then!) But
Ocaml uses the even stranger choice of "(* ... *)". Which means that you
can't write "(*)" to mean the multiplication function, you must write "(
* )" [with spaces] to prevent it parsing as a comment.
[Then again, Haskell has a glitch with the unary minus function...]
Also, Haskell uses "++" for string (and list) concatenation. But Ocaml
inexplicably uses "^" instead. (Haskell uses both "^" and "**" to mean
exponent - but one is with an integer exponent and the other is with an
arbitrary exponent. No, I can never remember which is which.)
Things don't get any better when you start looking at the build process.
In Haskell, if I want to compile a multi-module program, I say
ghc --make MyThing
GHC will then automatically build the entire dependency tree, detect
what libraries you need, check what (if anything) needs to be
(re)compiled, compile it, and link everything to give you MyThing.exe.
Hell, if a module has changed, it'll recompile it, but then it takes a
hash of the interface file. If the module's implementation has changed
but its implementation has not, the module will be recompiled, but
anything depending on it will not.
It can even spit out a makefile if you wish, automatically generated
directly from the source code. But usually, you just DON'T NEED make at
all. GHC does it all for you.
In Ocaml, things are not so easy. Apparently YOU have to manually
determine the correct order in which to compile things, and issue all
the commands in the right order. (Or just use make.) You even have to
compile and seperately link things - and tell the compiler whether it's
supposed to be compiling or linking, and manually tell it what libraries
to include. *sigh*
It gets even better though. If you write a source file and compile it,
*everything* inside is public. If you don't want that, you have to
*manually* copy and paste the names of the public stuff into an
"interface file", and you apparently have to manually write in the
correct type signatures. (So much for "Ocaml does automatic type
inference".) You then have to insure that this file is compiled at the
right moment - i.e., after the corresponding source file is compiled,
but before anything depending on it is compiled.
Again, in Haskell you just write a list at the top of the source file
saying what things should be public. The compiler does the rest. Even if
you're manually running the compiler stages for some reason, the right
thing happens. The object file and interface description are generated
automatically in a single pass, and dependencies won't compile until
these exist.
It is *claimed* that you can compile Ocaml into a library that can then
be linked into programs written in arbitrary other languages (C or
whatever). If so, that would certainly seem like an advantage. OTOH,
Haskell can supposedly do this too - it's just that it'll be a bloody
huge library! (Because it will contain a copy of the entire Haskell RTS.)
Actually, the latest version of GHC supports [on Linux only] dynamic
linking now, so a Haskell library can be dynamically loadable, and the
RTS can be dynamically loadable, and any other libraries you're using
can be dynamically loadable. So if your C program uses 5 Haskell
libraries, you still only have to link one (dynamic) copy of the RTS
[which is fairly big].
It seems everywhere I look, Ocaml does the same things that other
languages have done, but does them wrong. The bits that are right are
identical to existing languages. The bits that are new are almost all wrong.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Wait... you're kidding me.
In Haskell, you can write
foo = some function of bar
bar = some function of foo
But apparently in Ocaml, this produces a compile-time error, since when
foo is defined, bar hasn't been defined yet. Apparently you have to write
let rec foo = some function of bar
and bar = some function of foo
In other words, YOU have to manually resolve all circular dependencies
by hand.
WTF?
(I remind you, this is functional programming and recursive definitions
are ubiquitous...)
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Curried functions + optional arguments = oh dear. :-)
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Invisible <voi### [at] dev null> wrote:
> If you don't care about why Ocaml is wrong and why Haskell is better,
> you can stop reading now.
>
>
>
> As best as I can determine, Ocaml is some sort of hybrid of Java and
> Haskell. It's "mostly functional" and has syntax resembling Haskell, but
> it also has object-oriented features.
I stopped reading right here (though will still read the rest).
Haskell is not better, just different. And no, OCaml is not a hybrid of java
and haskell: it's a pragmatic OO successor to ML, which is the oldest static
typed, polymorphic functional language still in use today. Curried functions?
Hindley-Milner type system? Yeah, all coming from ML. You could say OCaml is
ML++. :)
Haskell is Junior, ML is Senior, capisce? ;)
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
nemesis wrote:
> Haskell is not better, just different.
Right. So the huge list of "Haskell does X automatically but Ocaml makes
you do it by hand" doesn't count as "better", just "different"?
> Curried functions?
> Hindley-Milner type system? Yeah, all coming from ML. You could say OCaml is
> ML++. :)
It even has "ML" right there in the name.
> Haskell is Junior, ML is Senior, capisce? ;)
Well, I've never seen ML, so I couldn't say if it's better or worse. But
Oaml seems to lack so many useful things...
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Invisible <voi### [at] dev null> wrote:
> Problems become apparent almost immediately. I had a look at the Great
> Language Shootout, where I found Ocaml getting royally OWNED by both C
> and C++, mainly because both of these used all four CPU cores, while
> Ocaml apparently can't. Ocaml supports multiple light-weight threads,
> but because the garbage collector isn't thread-safe, all Ocaml threads
> run in a single OS thread - i.e., on a single CPU core.
>
> Haskell, of course, runs your threads on several cores, like you'd
> expect. (Unless you specifically ask it not to because you're trying to
> interface to external C libraries that use thread-local storage.)
>
> Of course, even Haskell currently has a parallel but not concurrent GC,
> and in particular to perform a GC cycle you have to wait for all Haskell
> threads to stop. (They can only be stopped at certain points.) This can
> cause large program slowdowns, where one thread is in a tight loop and
> won't stop, and all the other threads are paused waiting to start a GC
> cycle.
You're right. The lack of concurrent GC is the single most important issue to
be fixed today, even Jon Harrop would tell that. That's why you're seeing today
C and C++ beating OCaml real hard. Because just a while ago -- when OpenCL,
OpenMP and other easily-parallelizing toolkits for C/C++ compilers weren't
generally available -- OCaml programs usually sported similar performance to
C/C++ and were also much clearer and shorter.
Eventually, it should get up to date.
> But hey, all of this isn't really to do with *Haskell*, but rather with
> *implementation*. Specifically, I'm talking about GHC. Other
> implementations do exist. [Most of them don't support SMP *at all*
> though.] You could write another implementation, or fix GHC, and the
> above drawbacks would go away.
yeah, but it's probably a very difficult issue.
> Then I started to look at the syntax. It's *mostly* identical to
> Haskell, but with a few differences.
Haskell is mostly identical to ML, which is OCaml's direct ancestor. Miranda
the most direct ancestor to Haskell was based on ML's ideas.
> In Haskell, I can write
>
> foo = 5
>
> In Ocaml, you must write
>
> let foo = 5
>
> Small bit of superfluous chatter there. Still, I guess it makes the
> syntax more consistent. Slightly. But then we get to the next
> abomination: In Haskell, I can say
>
> foo = bar foo
>
> but in Ocaml, you must say
>
> let rec foo = bar foo
let and letrec are common constructs in Scheme as well. They introduce scope
and recursively defined scope.
> Yes, that's correct, you have to manually tell the compiler that you're
> making a recursive definition. WTF? What, it isn't trivial enough for
> the compiler to detect this all by itself?
No, it's not trivial (oh such common word) at all. Which is also why you don't
have many compilers for Haskell, but you get quite a good lot of good performant
compilers for both ML (Jersey ML, MLton, OCaml) and Scheme (Bigloo, Chez,
Gambit, Stalin etc).
> Another puzzling thing is that Ocaml appears to use a layout rule
> similar to Haskell [not that the tutorial I'm following bothered to
> point this out], and yet it still requires explicit semicolons anyway.
> (??) And there's a set of fairly complex rules for when you must and
> must not write them. (?!?)
I don't like all the semicolons either. And the +. *. operators for
floats/doubles, but hey, at least these help on performance.
> In Haskell, you can say "Set Integer" or "Set String" or "Tree Integer"
> or whatever. Apparently in Ocaml you have to say this backwards:
> "integer tree" and so forth.
Being used to Pascal as well as C, I don't see the issue here. It's just a
different way to state something: "a set of integers" or "an integers set".
> Haskell uses the convention that type NAMES must begin with a capital
> letter, while type VARIABLE must begin with a lowercase letter. Ocaml
> uses the arguably superior convention that type variables start with an
> apostrophy.
I prefer Pascal and Scheme instance on the significance of names rather than
case. (though the latest Scheme standard has gone to case-sensitive lameness)
> Which reminds me - in Haskell, "Char" is a 32-bit Unicode character.
> Ocaml makes the mistake of using an 8-bit ASCII code. (But apparently
> there are libraries to "work around the problem".)
It was not a mistake before Unicode showed up, it was common-sense, specially in
a performance-conscious programming environment.
> Haskell has algebraic data types. Every Haskell type is nominally an
> ADT. (Although in the case of "Int32", it's an enumeration type with
> constructors named "0", "1", "2"...) All types follow the same uniform
> structure.
>
> Ocaml, on the other hand, has "records" which are like "{field1=5,
> field2=true, field3="hi"}", and "variants" which are like Haskell ADTs.
> And they're apparently not the same thing. You can see this when you
> define one:
>
> type recordX = {field1 : int, field2 : string, field3 : stuff}
>
> type variantY = Thing of int * string * stuff | OtherThing of char * char
>
> So "records" use named fields, but have only a single (unamed)
> constructor, while "variants" allow multiple constructors, but use a
> wierd syntax for defining the fields - a syntax resembling tuple types,
> actually. WTF?
I don't know enough of OCaml to say this with 100% certainty, but I'd say it's a
design choice geared at, again, performance. Just like C++ got plain structs
against the more heavyweight classes.
> Haskell makes the rather illogical choice of using "--" as the start
> marker for a comment. (Great. So I can't use that as a name then!) But
> Ocaml uses the even stranger choice of "(* ... *)". Which means that you
> can't write "(*)" to mean the multiplication function, you must write "(
> * )" [with spaces] to prevent it parsing as a comment.
Why would you write (*) 2 3 in OCaml when you can simple 2*3 or 2*.3? (or even
in Haskell, except to make it look like Lisp ;)
(* this comes from Pascal and BTW,
it spans several lines, which -- doesn't *)
Do you know what the Haskell multiline comment marker looks like? I'll wait for
the answer.
> Also, Haskell uses "++" for string (and list) concatenation. But Ocaml
> inexplicably uses "^" instead. (Haskell uses both "^" and "**" to mean
> exponent - but one is with an integer exponent and the other is with an
> arbitrary exponent. No, I can never remember which is which.)
Different syntaxes coming from different language heritages.
> In Haskell, if I want to compile a multi-module program, I say
>
> ghc --make MyThing
oh, you didn't get the fun times when GHC would compile down to C files and then
you ran the whole C-stack build process on your own, did you? You missed half
the fun, my friend...
> In Ocaml, things are not so easy. Apparently YOU have to manually
> determine the correct order in which to compile things, and issue all
> the commands in the right order. (Or just use make.) You even have to
> compile and seperately link things - and tell the compiler whether it's
> supposed to be compiling or linking, and manually tell it what libraries
> to include. *sigh*
OCaml development is not as large or hip as Haskell's, so they have to make use
of whatever is ready for them, which means things like make.
> It gets even better though. If you write a source file and compile it,
> *everything* inside is public. If you don't want that, you have to
> *manually* copy and paste the names of the public stuff into an
> "interface file", and you apparently have to manually write in the
> correct type signatures. (So much for "Ocaml does automatic type
> inference".)
well, it does, but just as in Haskell, the H-M type system some times has to
sort out type ambiguities that may arise. All but the simplest of modules don't
have type signatures. The interface files just collect such signatures, which
is good because it's the only thing I have to go through instead of wading
through large source code just to lookup for signature or, worse, try to
understand from source code what the hell a function is expecting for parameter.
> You then have to insure that this file is compiled at the
> right moment - i.e., after the corresponding source file is compiled,
> but before anything depending on it is compiled.
That's why a Makefile is so useful. ;)
> Again, in Haskell you just write a list at the top of the source file
> saying what things should be public. The compiler does the rest. Even if
> you're manually running the compiler stages for some reason, the right
> thing happens. The object file and interface description are generated
> automatically in a single pass, and dependencies won't compile until
> these exist.
Good for Haskell that its large developers have recreated make inside it, but
not everyone can afford that.
> It is *claimed* that you can compile Ocaml into a library that can then
> be linked into programs written in arbitrary other languages (C or
> whatever).
It can, because source files are compiled into native object code.
> If so, that would certainly seem like an advantage. OTOH,
> Haskell can supposedly do this too - it's just that it'll be a bloody
> huge library! (Because it will contain a copy of the entire Haskell RTS.)
yes, pragmatism was the main force behind OCaml.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
>> Problems become apparent almost immediately. I had a look at the Great
>> Language Shootout, where I found Ocaml getting royally OWNED by both C
>> and C++, mainly because both of these used all four CPU cores, while
>> Ocaml apparently can't.
>>
>> Of course, even Haskell currently has a parallel but not concurrent GC,
>
> You're right. The lack of concurrent GC is the single most important issue to
> be fixed today, even Jon Harrop would tell that. That's why you're seeing today
> C and C++ beating OCaml real hard. Because just a while ago -- when OpenCL,
> OpenMP and other easily-parallelizing toolkits for C/C++ compilers weren't
> generally available -- OCaml programs usually sported similar performance to
> C/C++ and were also much clearer and shorter.
>
> Eventually, it should get up to date.
I haven't checked yet, but I'm fairly sure the Shootout still has
benchmarks for older, single-core systems. If I check that it should be
informative.
>> But hey, all of this isn't really to do with *Haskell*, but rather with
>> *implementation*. Specifically, I'm talking about GHC. Other
>> implementations do exist. [Most of them don't support SMP *at all*
>> though.] You could write another implementation, or fix GHC, and the
>> above drawbacks would go away.
>
> yeah, but it's probably a very difficult issue.
Well, as difficult as it is for any language, yes.
>> Yes, that's correct, you have to manually tell the compiler that you're
>> making a recursive definition. WTF? What, it isn't trivial enough for
>> the compiler to detect this all by itself?
>
> No, it's not trivial (oh such common word) at all.
In exactly which way is it not trivial to determine that an identifier
appears in the LHS expression?
> Which is also why you don't
> have many compilers for Haskell, but you get quite a good lot of good performant
> compilers for both ML (Jersey ML, MLton, OCaml) and Scheme (Bigloo, Chez,
> Gambit, Stalin etc).
Right. And but because nobody knows Haskell exists yet?
(Besides, there are half a dozen compilers out there. It's just that
there's only one that's production-ready. Apparently there used to be more.)
> I don't like all the semicolons either. And the +. *. operators for
> floats/doubles, but hey, at least these help on performance.
So does [or should] statically-known types. If you statically know that
you're adding floats, replace + with +. and you're golden. It's not
rocket science. (Actually it's program optimisation...)
It seems several design choices in Ocaml are specifically about making
one special case faster at the expense of complexity and ugliness. Which
is fine, I suppose, if you're just trying to win benchmarks rather than
write maintainable software...
>> In Haskell, you can say "Set Integer" or "Set String" or "Tree Integer"
>> or whatever. Apparently in Ocaml you have to say this backwards:
>> "integer tree" and so forth.
>
> Being used to Pascal as well as C, I don't see the issue here. It's just a
> different way to state something: "a set of integers" or "an integers set".
It's valid I guess, but I'm still curious to see what happens when you
have multiple parameters...
>> Which reminds me - in Haskell, "Char" is a 32-bit Unicode character.
>> Ocaml makes the mistake of using an 8-bit ASCII code. (But apparently
>> there are libraries to "work around the problem".)
>
> It was not a mistake before Unicode showed up, it was common-sense, specially in
> a performance-conscious programming environment.
I'll admit that not that many people use Unicode. Still, I imagine it
means that if you try to adapt your program to work properly with
Unicode, it breaks all over the place in unexpected ways.
> I don't know enough of OCaml to say this with 100% certainty, but I'd say it's a
> design choice geared at, again, performance. Just like C++ got plain structs
> against the more heavyweight classes.
...except that C++ structs *are* classes. ;-)
Still, at least they didn't do that horrid C++ thing where you have to
manually specify which methods you can override later.
>> Haskell makes the rather illogical choice of using "--" as the start
>> marker for a comment. (Great. So I can't use that as a name then!) But
>> Ocaml uses the even stranger choice of "(* ... *)". Which means that you
>> can't write "(*)" to mean the multiplication function, you must write "(
>> * )" [with spaces] to prevent it parsing as a comment.
>
> Why would you write (*) 2 3 in OCaml when you can simple 2*3 or 2*.3? (or even
> in Haskell, except to make it look like Lisp ;)
You wouldn't. But if you want to pass "*" to a high-order function...
> (* this comes from Pascal and BTW,
> it spans several lines, which -- doesn't *)
>
> Do you know what the Haskell multiline comment marker looks like? I'll wait for
> the answer.
It's {- ... -}, which is vaguely more logical.
>> In Haskell, if I want to compile a multi-module program, I say
>>
>> ghc --make MyThing
>
> oh, you didn't get the fun times when GHC would compile down to C files and then
> you ran the whole C-stack build process on your own, did you? You missed half
> the fun, my friend...
Current versions of GHC default to using the native code generator
rather than GCC anyway. ;-)
>> In Ocaml, things are not so easy. Apparently YOU have to manually
>> determine the correct order in which to compile things, and issue all
>> the commands in the right order. (Or just use make.)
>
> OCaml development is not as large or hip as Haskell's, so they have to make use
> of whatever is ready for them, which means things like make.
In which universe is Haskell "hip"? o_O
Well anyway, I thought Ocaml was far better known than Haskell, but I
guess I could be wrong on that. It's just frastrating that they've
designed the system in such a way that even if you were prepaired to
write the code, it's not easy to automate.
> That's why a Makefile is so useful. ;)
Sure. But make doesn't determine dependancies; you have to do this
manually. And if you get it wrong, your program presumably just doesn't
link, or crashes when run. Kind of negates all the correctness benefits
of functional programming...
>> Again, in Haskell you just write a list at the top of the source file
>> saying what things should be public. The compiler does the rest.
>
> Good for Haskell that its large developers have recreated make inside it, but
> not everyone can afford that.
I repeat, make does not determine dependencies, it just decides what to
do based on them. (Which is admittedly non-trivial in itself.) As far as
I'm aware, there is no tool that writes makefiles for you. So GHC does
far, far more than make. (In fact, there's an option to have it write a
makefile for you, if you wish for some reason...)
>> It is *claimed* that you can compile Ocaml into a library that can then
>> be linked into programs written in arbitrary other languages (C or
>> whatever).
>
> It can, because source files are compiled into native object code.
Doesn't necessarily mean you can link to it from the outside. (I'm not
debating that you can, I'm just saying your explanation is incomplete.)
>> If so, that would certainly seem like an advantage. OTOH,
>> Haskell can supposedly do this too - it's just that it'll be a bloody
>> huge library! (Because it will contain a copy of the entire Haskell RTS.)
>
> yes, pragmatism was the main force behind OCaml.
Presumably native object code from Ocaml must also contain the Ocaml
runtime system too. (Including the GC, at least...)
Being pragmatic is fine - isn't that what made C so popular in spite of
being rubbish? But it seems to be that there are a lot of poor design
choices here that don't enhance performance in any way, they just make
programming harder. Which is somewhat dissapointing.
I'm not saying Ocaml is completely rubbish. It does seem to have a small
few interesting ideas in it. (The tutorial I read doesn't really explore
these very much...) I just hope I never have to actually use it. :-}
--
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Invisible wrote:
> integer * string
> which seems a rather perverse choice.
Cartesian product of integers and strings. I'm not sure what a "sum type"
would be.
> instead of "(True, 5)"? It would be clearer...)
Wait till you get to python, where the tuple constructor is actually the
comma, the parens are optional, and you try to figure out how to write a
one-element tuple literal, or the empty tuple.
> Haskell makes the rather illogical choice of using "--" as the start
> marker for a comment.
Common in a lot of other languages like SQL and Ada. FWIW.
> Ocaml uses the even stranger choice of "(* ... *)".
Which is Pascal for comments, if your keyboard (punched cards) don't have
curly braces. FWIW.
--
Darren New, San Diego CA, USA (PST)
Forget "focus follows mouse." When do
I get "focus follows gaze"?
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
>> integer * string
>> which seems a rather perverse choice.
>
> Cartesian product of integers and strings. I'm not sure what a "sum
> type" would be.
Either Int String
is a sum type. The set of possible values is the *sum of* the set of
possible Int values and the set of possible String values.
Whereas... well, you understand what a Cartesian product is already, right?
Haskell's "algebraic data types" are sum types of product types. (The
sum may contain only one summand, but a sum none the less. Actually, if
you enable the EmptyDataDecls extension, zero summands are permissible...)
>> instead of "(True, 5)"? It would be clearer...)
>
> Wait till you get to python, where the tuple constructor is actually the
> comma, the parens are optional, and you try to figure out how to write a
> one-element tuple literal, or the empty tuple.
Oh, the *type constructor* for a 2-tuple is "(,)", and for a 3-tuple
it's "(,,)".
So if you want to be 73% anal, you can write "(,) String Integer"
instead of "(String,Integer)". :-}
Off the top of my head, I don't *believe* this works with value
constructors... No, wait. I'm wrong; it does work.
(,,) :: a -> b -> c -> (a,b,c)
This means we can write
zip = zipWith (,)
Eat THAT and smoke it! o_O
(Similarly, "[Char]" can also be written "[] Char".)
>> Haskell makes the rather illogical choice of using "--" as the start
>> marker for a comment.
>
> Common in a lot of other languages like SQL and Ada. FWIW.
I know Eiffel uses it. But then, Eiffel is weird.
>> Ocaml uses the even stranger choice of "(* ... *)".
>
> Which is Pascal for comments, if your keyboard (punched cards) don't
> have curly braces. FWIW.
...more useless information to add to my collection! o_O
Then again, in Pascal "(*)" is not a meaningful thing to write in the
first place. ;-)
PS. I'm loving the way Haskell uses curly brackets for explicit
grouping, AND ALSO for named-field syntax. Way to use the exact same
symbol for two unrelated things that you might want to do AT THE SAME TIME!
--
http://blog.orphi.me.uk/
http://www.zazzle.com/MathematicalOrchid*
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Orchid XP v8 wrote:
> Either Int String
Obvious once stated.
> Oh, the *type constructor* for a 2-tuple is "(,)", and for a 3-tuple
> it's "(,,)".
Not in Python. The cosntructor for a 2-tuple is ,
No parens needed. Figure *that* one out. :-)
>> Common in a lot of other languages like SQL and Ada. FWIW.
> I know Eiffel uses it. But then, Eiffel is weird.
That too. All the "readable" languages tend to use it, because it's how you
offset comments in English -- that is, if you have a comment to make.
--
Darren New, San Diego CA, USA (PST)
Forget "focus follows mouse." When do
I get "focus follows gaze"?
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |