I have some time to kill and I was thinking of satisfying a long time
curiosity of mine: learn (and actually start using for a change) a
functional programming language. Right now i am kind of trying to decide
between 3 languages: erlang, haskell and ocaml. This post is not intended to
start yet another flame war on which language is actually better and why is
that again.
I realize that Haskell programmers will prefer Haskell, erlang
programmers... etc. I have been hanging around this forum for a while now
and while i have never used any of these languages, i have some vague idea
about their respective strengths and weaknesses (i think).
So I guess the question is, is the following characterization of these 3
languages more or less accurate, and what additional points am i missing
that may be of importance?
Erlang.
I like erlang because it's built on a fresh idea. The combination of single
assignment, prolog-style unification and process-based computing is very
appealing, and having a more or less in-built database in the language is a
big plus. On the minus side, I am a bit worried that because of the very
specific orientation towards distributed computing, erlang does not make a
good general-purpose programming language. Also, it seems string processing
in erlang is tedious and inneficient. comments?
Haskell
It may be my mistaken impression, but it seems Haskell is the most used
functional language of the three. It seems to have a very solid foundation,
with lots of stable libraries and links to other environments (which is very
important for a general purpose programming language to me). I must admit i
have no clear idea about what 's under the hood of haskell (is it
interpreted / compiled?). Also, i've heard the remark that Haskell is very
theoretical / academic in origin. I guess that goes for most functional
languages, but I've heard horror stories about monads and things like that.
feel free to discuss...
Ocaml
I think Ocaml is very attractive because of its performance. Also, the OO
part is nice, it makes the switch to functional languages seem less scary i
think :-). Also, the fact that it has both compilers and interactive
interpreters is nice for people starting out with the language. On the down
side, i 've heard people call it hella ugly on numerous occasions... hehe.
I am not asking anyone to somehow "help me choose" a language on the basis
of this, I am just trying to get some orientation here. An ideal language
for me would cover the following points (but i know there's no such a
thing):
- a somewhat declarative / intuitive syntax. Unreadable source code takes a
lot of the fun out of programming.
- performance. enough said.
- the ability to have a program both compiled or interpreted. interactive
shells are nice for learning.
- powerful string processing. (although this may not be the area where
functional languages shine, i don't know)
- a GUI front-end. (Tk - link would be nice :-)
- portability
- a broad user base ( = lots of available modules and libraries, good
documentation and tutorials, ...)
- an activeX/com interface on the windows platform would be very nice...
- graphics programming modules would be nice too, come to think of it...
- ...
... I think you get the idea. I'm sorry this post turned out to be a bit
more bulky than i thought. I really appreciate whatever insights, comments,
elaborations or random thoughts you may have. (go easy on the flames, i'm a
functional newbie... :-)
Thanks a lot for your time,
stijn.
> Erlang.
> I like erlang because it's built on a fresh idea. The combination of single
> assignment, prolog-style unification and process-based computing is very
> appealing,
<snip>
> On the minus side, I am a bit worried that because of the very
> specific orientation towards distributed computing, erlang does not make a
> good general-purpose programming language.
It is difficult to convey to people who haven't really tried it
how powerful the concurrency paradigm used by Erlang is -- not
just for distributed computing (even though it's a real life saver in
that niche), but as a general purpose modeling paradigm. You may
want to read Joe Armstrong's PhD thesis, "Making Reliable Systems
in the Precense of Software Errors",
http://www.sics.se/~joe/thesis/armstrong_thesis_2003.pdf
Once properly brainwashed, you will gladly contribute to the
http://www.bluetail.com/wiki/showPage?node=RavingsAboutOop
page on the erlang wikie. ;-) You'll find that the combination
of lighweight processes (done properly) and functional programming
will relieve you of any desire to use OO as anything other than
a sometimes handy abstraction for specific problems.
Also on Joe's home page, you may take a look at his prototype
X11 widget library for Erlang -- IMO a very attractive model
for GUI programming. This is one example of Erlang used for something
other than distributed programming. Another (anecdotal) example is,
say, the Ericsson AXD 301 Multi-Service Gateway, which contains ca
2 million lines of erlang code performing quite a wide variety of
functions.
> Also, it seems string processing in erlang is tedious and inneficient.
Alas, this is not entirely false. Inefficient, yes, both in space and
time, as strings are represented as linked lists of bytes, each byte
taking up 8 bytes of memory (cons cells, etc.).
String processing is not necessarily tedious, though, unless you
compare with e.g. Perl. In some ways, it's really quite elegant.
If you want to do Unicode, you're on your own - no built-in support,
and only a few Open Source libraries to fall back on.
There's no getting around it, though -- string processing is not
exactly Erlang's forte. (:
/Uffe
--
Ulf Wiger, Senior Specialist,
/ / / Architecture & Design of Carrier-Class Software
/ / / Strategic Product & System Management
/ / / Ericsson AB, Connectivity and Control Nodes
I would miss static typing from erlang, it is quite slow IMHO. Unless for
very special, massively distributed tasks If runtime and static typing
does not matter, I would prefer Ruby or Python over Erlang.
>
> Haskell
> It may be my mistaken impression, but it seems Haskell is the most used
> functional language of the three. It seems to have a very solid foundation,
My impression is exactly the opposite. I have seen large and useful
projects in both Erlang and OCaml. The only large and interesting Haskell
project I know about is GHC.
> theoretical / academic in origin. I guess that goes for most functional
> languages, but I've heard horror stories about monads and things like that.
> feel free to discuss...
Haskell has the nicest syntax and semantics of the 3 langauges. Monads
are especially usable and elegant and there is nothing very complicated
about them. They can be easily undestood and used.
Biggest problem about Haskell is lazy evaluation, which can make
the memory consumption of larger projects quite unpredictable. However,
this may also be caused by implementation problems, since Clean seems
to be lazy, performant and predictable at the same time.
> Ocaml
> I think Ocaml is very attractive because of its performance. Also, the OO
> part is nice, it makes the switch to functional languages seem less scary i
> think :-). Also, the fact that it has both compilers and interactive
> interpreters is nice for people starting out with the language. On the down
> side, i 've heard people call it hella ugly on numerous occasions... hehe.
OCaml's syntax is uglier than that of Haskell, but it is still much nicer
that that of C or Java and most mainstream languages. I have once written
a small interpreter in OCaml in a matter of days, and I was told that the
code is extremely readable. I also have the impression that ocaml has
a very good balance between pragmatism and theoretical well-foundedness.
For the first time it is extremely hard to get used to it. It took me
a whole day to write my first 15 lines in Ocaml. OTOH I wrote my fourth
program in a day also, but it had 500 lines and I would not have
been able to write the same stuff in C in a week and under several 1000
line of code.
Moreover, I found ocaml more stable and faster than Haskell. I would judge
that there are more (and more mature) librearies for OCaml than for Haskell.
I'd be keen on using Haskell, but I don't think it's mature enough for
most tasks.
> - powerful string processing. (although this may not be the area where
> functional languages shine, i don't know)
OCaml's camlp4 frontend (standard part of the distribution)
allows extending the language in a safe but powerful way.
Using it, the language can be extended by intuitive
regexp and here-document support. The nice thing about camlp4,
that these extensions remain usable even in the interactive shell
and the error messages are also readable.
> - a GUI front-end. (Tk - link would be nice :-)
OCaml has a very usable GTK binding. Haskells bindings are much less
mature IMHO.
> - portability
All of them are quite portable.
> - a broad user base ( = lots of available modules and libraries, good
> documentation and tutorials, ...)
Erlang has the best docus. OCaml is not bad either, but not comparable
to Erlang. I would say that Haskell is the least documented of the three.
Each of them has a small, but stable user-base. I think, ocaml is most
popular for practical general-purpose programming.
> Also on Joe's home page, you may take a look at his prototype
> X11 widget library for Erlang
Do you have a pointer to that - I've found 3 of Joe's home pages, and I
can't find the widget library on any of them.
Thanks
- ken
Haskell has a clear advantage here, at least in comparison to Erlang or
Ocaml.
It's a bit strange since indentation is significant (not a problem but
something to get used to) (it's not a real issue though).
> - performance. enough said.
If Java on a VM is good enough for you, any of the three languages will
qualify.
If you need fast matrix multiplications or graphics processing, OCaml is
fastest, Haskell second (it does have array processing capabilities,
it's just that they aren't mentioned in introductory texts). In Erlang,
such applications are a case for dropping to C; calling C from Erlang
isn't difficult, but setting up the required interface machinery is a
lot of work (Erlang favored isolation over ease of set-up, and you have
to pass all the data through the isolation layer).
> - the ability to have a program both compiled or interpreted. interactive
> shells are nice for learning.
If JVM performance is good enough for you, then you don't need this.
Interpreted code can be quite fast anyway.
That said, all three can compile to machine code, though it's not in
widespread use for Erlang (not sure why exactly, either because it's
relatively fresh, or because there are incompatibilities, or because
it's not free - I'm sure Uffe could clarify *g*).
OCaml is generally considered to generate the fastest machine code.
I don't think there is an interpreter for it.
Haskell offers several compilers, which are mutually (slightly)
incompatible - whether the incompatibilities will bite you depends
heavily on what mechanisms you use.
There are interpreters available. They consist of a read-eval-print
loop, with some editing facilities and rudimentary browser capabilities
("rudimentary" in comparison to what is available in a typical Lisp or
Smalltalk system). That's good enough for experimentation, but the main
work is still done in the editor.
> - powerful string processing. (although this may not be the area where
> functional languages shine, i don't know)
Functional languages typically handle strings as lists of concated
numbers. In most cases, this means a memory overhead of 500%-700% (AFAIK
that's true for Haskell and Erlang, I don't know how exactly OCaml
handles strings, and there may be better mechanisms available in Haskell).
String *manipulation* generally tends to be slower. In principle, an FPL
must keep the old string and create the new, modified string, so
single-character changes either require massive library support or are
extremely slow; the consequence: don't change characters one at a time,
generate the string that you want in one go :-)
String extraction is just as fast as in imperative languages - both
typically process a string by scanning it character by character.
> - a GUI front-end. (Tk - link would be nice :-)
Dunno.
> - portability
All run on both Windows and Unix.
All have "foreign function interfaces", though they work very differently:
* Erlang uses messages to communicate with C software
* OCaml does traditional direct calls to C routines
* Haskell wraps external calls in the IO monad
> - a broad user base ( = lots of available modules and libraries, good
> documentation and tutorials, ...)
Not sure about that.
> - an activeX/com interface on the windows platform would be very nice...
Not available AFAIK.
ActiveX/Com is an exceedingly complex interface with mediocre
documentation, you need several thousand lines of glue code just to use
it. IOW using it borders on Black Magic.
Any language that's first implemented on Unix will acquire ActiveX/Com
late, and AFAIK none of the languages under discussion has it.
Haskell skipped Com and was ported to .net (that work was sponsored by
Microsoft). I don't know what the current status of that port is, there
haven't been any recent announcements for it.
Regards,
Jo
--
Currently looking for a new job.
> Stijn De Saeger wrote:
> > - a somewhat declarative / intuitive syntax. Unreadable source code
> > takes a lot of the fun out of programming.
>
> Haskell has a clear advantage here, at least in comparison to Erlang
> or Ocaml.
> It's a bit strange since indentation is significant (not a problem but
> something to get used to) (it's not a real issue though).
It shouldn't cause a problem reading code and you aren't required to use
it if you don't want to when writing code. The only time it would be an
issue is when editing code created by others.
> > - performance. enough said.
>
> If Java on a VM is good enough for you, any of the three languages
> will qualify.
> If you need fast matrix multiplications or graphics processing, OCaml
> is fastest, Haskell second (it does have array processing
> capabilities, it's just that they aren't mentioned in introductory
> texts). In Erlang, such applications are a case for dropping to C;
> calling C from Erlang isn't difficult, but setting up the required
> interface machinery is a lot of work (Erlang favored isolation over
> ease of set-up, and you have to pass all the data through the
> isolation layer).
>
> > - the ability to have a program both compiled or interpreted.
> > interactive shells are nice for learning.
>
> If JVM performance is good enough for you, then you don't need this.
> Interpreted code can be quite fast anyway.
>
> That said, all three can compile to machine code, though it's not in
> widespread use for Erlang (not sure why exactly, either because it's
> relatively fresh, or because there are incompatibilities, or because
> it's not free - I'm sure Uffe could clarify *g*).
>
> OCaml is generally considered to generate the fastest machine code.
> I don't think there is an interpreter for it.
>
> Haskell offers several compilers, which are mutually (slightly)
> incompatible - whether the incompatibilities will bite you depends
> heavily on what mechanisms you use.
They should all agree (except where explicitly documented) on Haskell
98, if not it's a bug.
> There are interpreters available. They consist of a read-eval-print
> loop, with some editing facilities and rudimentary browser
> capabilities ("rudimentary" in comparison to what is available in a
> typical Lisp or Smalltalk system). That's good enough for
> experimentation, but the main work is still done in the editor.
>
> > - powerful string processing. (although this may not be the area
> > where functional languages shine, i don't know)
>
> Functional languages typically handle strings as lists of concated
> numbers.
I don't think this is typical and in Haskell's case, strings are lists
of characters not numbers. *Prolog nightmare flashbacks*
> In most cases, this means a memory overhead of 500%-700%
> (AFAIK that's true for Haskell and Erlang, I don't know how exactly
> OCaml handles strings, and there may be better mechanisms available in
> Haskell).
GHC provides PackedStrings which is more efficient. Technically,
you should be able to roll a decent PackedString-like library using the
utility libraries of the FFI.
> String *manipulation* generally tends to be slower. In
> principle, an FPL must keep the old string and create the new,
> modified string, so single-character changes either require massive
> library support or are extremely slow; the consequence: don't change
> characters one at a time, generate the string that you want in one go
> :-) String extraction is just as fast as in imperative languages -
> both typically process a string by scanning it character by character.
>
> > - a GUI front-end. (Tk - link would be nice :-)
http://www.haskell.org/libraries/#guigs
> > - portability
>
> All run on both Windows and Unix.
> All have "foreign function interfaces", though they work very
> differently:* Erlang uses messages to communicate with C software
> * OCaml does traditional direct calls to C routines
> * Haskell wraps external calls in the IO monad
Not necessarily and at any rate the IO monad is used for any IO so this
says almost nothing about the FFI. At any rate, Haskell has a standard
FFI which is quite simple to use and people I know that have used both
it and OCaml's say it (much) simpler/nicer than OCaml's. However, this
is second-hand anecdotal information so take it for what it's worth.
> > - a broad user base ( = lots of available modules and libraries,
> > good documentation and tutorials, ...)
>
> Not sure about that.
>
> > - an activeX/com interface on the windows platform would be very
> > nice...
>
> Functional languages typically handle strings as lists of concated
> numbers. In most cases, this means a memory overhead of 500%-700% (AFAIK
> that's true for Haskell and Erlang, I don't know how exactly OCaml
> handles strings
Ocaml stores strings in custom allocated memory blocks wasting as much as
3 bytes per string. (cause of alignment). There is also a
standard library with an implementation of regular expressions
> OCaml does traditional direct calls to C routines
Not quite, you need to write simple, boring wrappers around the "pure" (what a
irony!) C routines to call them from the Ocaml code.
Besides, now, thanks to Richard Jones, you can use the Perl
modules/libraries from you Ocaml code. (Never used them, though)
Regards,
vasha
Sure, there is a very concise interpreter for OCaml. And it is
well integrated with the preprocessor. So, you can extend the ocaml
frontend in a very safe manner (e.g. by more concise regular
expressions, here documents, html-templating, XML pattern-matching,
perl like hashtables, GUI generators etc.) and the extended syntax
will work in the interpreter also. You just load the new frontend
libraries in the interpreter like normal DLL-s, and that's it.
OCaml has also fancy runtime loading mechanism for bytecode
and even machine code compiled program may load and run
bytecode compiled stuff. OCaml's bytecode is the fastest of all
languages I know. It's faster than Lisp, or JIT run Java.
And the best: it has a very fancy debugger, which allows you to
to step backwards in bytecode compiled programs.
I had made some tests with the bytecode threads of OCaml. They
are very fast also. Much faster than the native threads. So, if
you have a massive multithreading program, bytecode may be faster
than machine code. I generated some 10000s of threads, and the
bytecode version scaled perfectly. The native-threaded version
was completely unusable when opened more than 1000 threads.
>
> Functional languages typically handle strings as lists of concated
> numbers. In most cases, this means a memory overhead of 500%-700% (AFAIK
> that's true for Haskell and Erlang, I don't know how exactly OCaml
> handles strings, and there may be better mechanisms available in Haskell).
OCaml has native string support, so you don't pay any memory
or runtime penalty. You can easily convert strings to lists and
backward, but the string processing library is powerful enough,
so that you practically never need this.
OCaml is not unicode-capable out of box like Java (which is even
good for raw performance), but there are unicode handling libraries,
so unicode string processing (regular expressions,lexer genarators,
converting between encodings, etc.) is not a problem in OCaml.
However, you can enter raw unicode string literals in ocaml,
because the lexer of the OCaml compiler is strictly ASCII (I think
this will change soon), but you will get much bigger problems when
trying to use and process unicode with Erlang or Haskell.
> Not quite, you need to write simple, boring wrappers around the "pure" (what a
> irony!) C routines to call them from the Ocaml code.
SWIG can automatically generate those calls.
There is. As for fastest machine code (i.e., most efficient,
as I assume it all executes at processor speed), the way to
go is declare C a functional language. It isn't a very good
one, but that's a different problem.
I don't know how ocaml programs rank, in general, but I believe
I've seen competitions where the ocaml entry falls back on
low level imperative programming. That isn't a crime, but it's
something to think assuming you don't like my idea about C.
> Functional languages typically handle strings as lists of concated
> numbers. In most cases, this means a memory overhead of 500%-700% (AFAIK
> that's true for Haskell and Erlang, I don't know how exactly OCaml
> handles strings, and there may be better mechanisms available in Haskell).
Character string is a native ocaml type. Mutable.
> String *manipulation* generally tends to be slower. In principle, an FPL
> must keep the old string and create the new, modified string, so
> single-character changes either require massive library support or are
> extremely slow; the consequence: don't change characters one at a time,
> generate the string that you want in one go :-)
> String extraction is just as fast as in imperative languages - both
> typically process a string by scanning it character by character.
I don't know if you're talking about string lists here, but
the string as list approach has its uses. I don't know if
it's what you'd call `string manipulation', but a parsing
sort of analysis that proceeds on a character by character
bases from start to end seems fairly efficient to me - it
exploits the head:tail structure of a list - and I wouldn't
be surprised to see it turn out noticeably faster than the
same problem in a language with an immutable native string.
Donn Cave, do...@u.washington.edu
What !? you can call perl scripts / modules from Ocaml? I use perl (that 's
why i get so picky about string processing... hehe) A statue for that man !
stijn.
>
> Regards,
> vasha
The only functional language compiler that I have ever been able
to make work on a not-officially-supported platform myself was
ocaml. nhc98 looks promising, but so far no joy.
Donn Cave, do...@drizzle.com
Concurrent Haskell threads are also very fast, at least those
implemented in GHC.
Unfortunately, in current implementation some synchronisation primitives
have unacceptable cost. For example, QSem's (quantity semaphore)
operations waitQSem and signalQSem have a cost proportional to a number
of threads waiting on a semaphore. This is quite easy to fix, but as it
is now it can be somehow discouraging.
>> Functional languages typically handle strings as lists of concated
>> numbers. In most cases, this means a memory overhead of 500%-700% (AFAIK
>> that's true for Haskell and Erlang, I don't know how exactly OCaml
>> handles strings, and there may be better mechanisms available in Haskell).
>
> OCaml has native string support, so you don't pay any memory
> or runtime penalty. You can easily convert strings to lists and
> backward, but the string processing library is powerful enough,
> so that you practically never need this.
I consider OCaml's imperative strings an ugly hack. Their imperativeness
can lead to very surprising bugs. Consider this:
# let f = function
| 0 -> "zero"
| 1 -> "one"
| 2 -> "two"
| _ -> "other"
;;
val f : int -> string = <fun>
# (f 0).[0] <- 'X' ;;
- : unit = ()
# (f 1).[0] <- 'Y' ;;
- : unit = ()
# f 0 ;;
- : string = "Xero"
# f 1 ;;
- : string = "Yne"
# f 2 ;;
- : string = "two"
# f 3 ;;
- : string = "other"
Funny, yes, but functional...?
Haskell has all features needed to support efficient strings. It has
unboxed arrays, unboxed mutable arrays, encapsulated local state threads
(ST monad), etc. All it lacks are mature libraries that help using these
features for string processing. (I know about PackedString, but I don't
consider it mature - there is no hGetLinePS).
I think that today Haskell development focus is on providing general
mechanisms that allow creating features like "fast strings" directly in
the language, not necessarily making them a language primitive (like in
OCaml).
Best regards,
Tom
--
.signature: Too many levels of symbolic links
Apologies. Here it is:
Note that constructs like "Entry !! read" is Joe's
personal extension to Erlang, and is syntactical shorthand
for an rpc call. It's only present in the documentation
though, not the downloadable source code.
Thus,
Display = ParentPid !! display
in the documentation becomes
Display = rpc(ParentPid, display)
in the code.
Readers a bit unfamiliar with functional programming in
general, and Erlang in particular, may want to browse through
the source code for the widgets (it's quite brief), and note
the nearly complete absense of error handling code.
Instead, the code is written so that it will either perform
correctly or crash. When using lots of processes, and Erlang's
process linking concept, this means that cleanup is automatic
and the work can either be restarted or aborted at the
appropriate level.
In the Erlang community, this is a pervasive design philosophy
called "programming for the correct case", or "the Let it Crash
Philosophy". It has a dramatic impact on code readability,
especially in systems that must be very fault-tolerant and
run non-stop (something that is, but shouldn't be, a fairly
rare requirement.)
A more thorough discussion of this is found in Joe's
excellent thesis (link in previous post.)
This is wrong, OCaml has an interpreter. You can compile OCaml to machine
code, byte code (i.e. there is a byte code interpreter) and use the
toplevel interpreter for running scripts directly.
> Functional languages typically handle strings as lists of concated
> numbers. In most cases, this means a memory overhead of 500%-700% (AFAIK
> that's true for Haskell and Erlang, I don't know how exactly OCaml
> handles strings, and there may be better mechanisms available in Haskell).
OCaml handles strings efficiently, i.e. represents them internally the
same way as C.
> String *manipulation* generally tends to be slower. In principle, an FPL
> must keep the old string and create the new, modified string, so
> single-character changes either require massive library support or are
> extremely slow; the consequence: don't change characters one at a time,
> generate the string that you want in one go :-)
You can overwrite string characters in OCaml directly.
> String extraction is just as fast as in imperative languages - both
> typically process a string by scanning it character by character.
Or faster ;-)
I'd say that what concerns string processing, OCaml is most likely the
most useful FPL and can surely also compete with Perl/Python/Ruby in
terms of flexibility, tool- (ocamllex + ocamlyacc) and library support
(Str (standard), Pcre (Perl regexps), Regexp (DFAs)). Not to mention
efficiency: it would be an understatement to say that OCaml outperforms
those languages...
>> - a GUI front-end. (Tk - link would be nice :-)
> Dunno.
There are pretty fully-featured libraries for Tk and GTK for OCaml.
>> - an activeX/com interface on the windows platform would be very nice...
> Any language that's first implemented on Unix will acquire ActiveX/Com
> late, and AFAIK none of the languages under discussion has it.
OCaml has tools to automatically generate COM-interfaces:
Regards,
Markus Mottl
--
Markus Mottl http://www.oefai.at/~markus mar...@oefai.at
Btw., has anybody already tried Linux 2.6 with heavily threaded
OCaml-programs? The new POSIX-library and scheduler have been greatly
improved so native threads should scale much better now.
It's all a matter of how big the differences really are. In my experience
it is seldom the case that OCaml-programs take longer than 2x. The
maximum I have ever seen was about 4x, the average case on _highly_
optimized and hand-crafted algorithms (both C/gcc and OCaml) is about
30%. This is definitely good enough for almost all programming tasks. You
should additionally consider that OCaml makes it much easier to implement
complicated, efficient algorithms.
> That said, all three can compile to machine code, though it's not in
> widespread use for Erlang (not sure why exactly, either because it's
> relatively fresh, or because there are incompatibilities, or because
> it's not free - I'm sure Uffe could clarify *g*).
Well, I can try. (:
<boast purpose="background">
I claim that there is no available middleware, commercial
or otherwise, that comes even close to Erlang/OTP in the
area of telecoms and datacom product development. Both in
terms of product quality and level of support, Erlang/OTP
runs circles around anything I've seen (not that there are
really any similar middleware offerings to my knowledge.)
The OTP team offers outstanding support and has _never_
missed a release date since the start in 1996. I say this
representing the biggest paying customer of OTP, not as
a team member (which I'm not, and never have been.)
</boast>
If AXD 301, the product that has been the biggest driver
of OTP development to-date, had used native code compilation
more, it might have both developed faster and caught on
faster. But in the beginning, a problem for both HiPE and
ETOS was that they couldn't compete with the reliability,
support and functionality of the Erlang/OTP suite. For
HiPE, this point is nowadays moot -- se below.
In fact, BEAM (the Erlang/OTP VM) initially compiled to C,
which was then compiled by gcc to native code. We used that
in AXD 301, but eventually struck a deal with OTP that they
could remove the native code compilation if they could
optimize the "threaded code" execution to the point that
we met our performance requirements anyway. The OTP team
pulled it off, and we allowed them to throw out the
native code stuff, radically simplifying maintenance.
In 1999, Thomas Lindgren evaluated HiPE on AXD 301 code
http://www.docs.uu.se/astec/Reports/Reports/9904.ps
Interestingly, while HiPE could outperform BEAM by
3 to 8 times on benchmarks like fib, huff, nrev, qsort
et al, it was only 25% faster when applied to a subset
of the most performance-critical parts of the AXD 301
application, and the memory consumption increased.
In later evaluations, we've seen that there was no
performance benefit at all from compiling to native.
This is a moving target (both HiPE and our code), so
we try to revisit the issue now and then.
I will try to be absolutely fair to the HiPE team.
Much of the above can be explained:
- The OTP team has continuously adopted optimizations
developed by the HiPE team, and BEAM and HiPE now
use exactly the same compiler with different backends.
Thus, quite a few speed improvements in Erlang are
due to the work of the HiPE team.
- HiPE is no longer a competing product, but is fully
integrated into Erlang/OTP, so I suspect that its
use will spread, perhaps quietly, now that you don't
have to choose between OTP and HiPE. You can even
decide per function to compile to native code.
- AXD 301 has been quite carefully optimized to get the
most out of BEAM, and BEAM has to some extent been
optimized to run AXD 301 code as efficiently as
possible.
- The HiPE team has always asked for sample code that
exercises the system in a _realistic_ manner, but
for e.g. AXD 301, this is not so easy to deliver
(2 million lines of Erlang, 2 million lines of C,
and custom hardware -- it's difficult both to
package in a portable way and to simulate in a neat
benchmark program.) The code they measured on in '99
is now obsolete, and doesn't reflect our application.
- Recent improvements by the HiPE team could probably
speed up AXD301 measurably, but it sort of comes
with the territory to be a bit conservative when you
have a product with > 99.999% availability and
_sufficient_ performance. (:
If you read this far, I'm impressed.
>
> I consider OCaml's imperative strings an ugly hack. Their imperativeness
> can lead to very surprising bugs. Consider this:
...
> Funny, yes, but functional...?
OCaml is pragmatic, it is and never was *purely* functional.
Once you introduce references and mutable data members, you
have a mixed environment. However, this can make life
easier in praxis.
> Christian Szegedy wrote:
> > I had made some tests with the bytecode threads of OCaml. They
> > are very fast also. Much faster than the native threads. So, if
> > you have a massive multithreading program, bytecode may be faster
> > than machine code. I generated some 10000s of threads, and the
> > bytecode version scaled perfectly. The native-threaded version
> > was completely unusable when opened more than 1000 threads.
>
> Concurrent Haskell threads are also very fast, at least those
> implemented in GHC.
Since we're comparing Erlang, OCaml and Haskell, Erlang threads
cost about 15-20 us to create on my clunky 400 MHz UltraSPARC,
sending a message costs about 4 us, and these numbers stay
constant at least up to 200,000 simultaneous threads. The current
hard limit in Erlang is 262,143 processes, and this limit is
mainly related to backward compatibility in distributed environments.
However, if you really intend to use a large number of threads
for anything other than very simple tasks, your programming
model will become extremely important.
> Unfortunately, in current implementation some synchronisation primitives
> have unacceptable cost. For example, QSem's (quantity semaphore)
> operations waitQSem and signalQSem have a cost proportional to a number
> of threads waiting on a semaphore. This is quite easy to fix, but as it
> is now it can be somehow discouraging.
I am convinced that for complex concurrency, you'll want to avoid
shared data and things like semaphores and monitors. You'll also
want to avoid event-based programming with run-to-completion
semantics. Otherwise, you may well end up feeling like the
famous frog in water that's slowly being heated to boiling
temperature -- not so bad in the beginning, but it will
eventually kill you, and you won't get a distinct cue as
to when you should bail out.
This is BTW my main gripe about Scala: it inherits Java's
broken concurrency support, making it uninteresting for
any application with significant concurrency issues,
nonwithstanding the cute Erlang-like actor example in the
beginning of the manual. You should have built on that and
left Java's concurrency out (difficult, given that you want
the close marriage with Java, I know, but in terms of
concurrency support, this marriage is very unfortunate.)
Well, there is a compiler (Hype) that works nicely for speeding up
"algorithmic" code in Erlang, though you give up a few features by
using it. With it, I'm happy with the speed of Erlang. (And
incidentally, as I'm using Python heavily right now, hype does for
Erlang what Psyco does for Python, without which it would be even
worse in my experience than "uncompiled" Erlang.)
And there's been and is ongoing more ambitious work on static typing
for Erlang, by e.g. colleagues in my department, but nothings been
published yet. Drop me a line and I'll put those interested in touch.
Stefan,
--
Stefan Axelsson (email at http://www.cs.chalmers.se/~sax)
I think this is the key point - this makes both options available to the
average developer.
I wasn't aware that HiPE (the Erlang machine-code compiler) is part of OTP.
> You can even decide per function to compile to native code.
That's an interesting point.
> If you read this far, I'm impressed.
Thanks ;-)
Does anybody have a good, comprehensive list of what's broken about
Java's concurrency support?
Not that I'm happy with it myself, but I have no clear idea what exactly
is broken, and hence no idea when to apply what workarounds, or what
extra tests to plan for.
> Ulf Wiger wrote:
> > - HiPE is no longer a competing product, but is fully integrated into
> > Erlang/OTP,
>
> I think this is the key point - this makes both options available to the
> average developer.
> I wasn't aware that HiPE (the Erlang machine-code compiler) is part
> of OTP.
For details on this, and the different options in HiPE, you
may read "All you wanted to know about HiPE
(but might have been afraid to ask)", by Sagonas et al,
presented at the Erlang User Conference 2003:
http://www.erlang.se/workshop/2003/paper/p36-sagonas.pdf
> Ulf Wiger wrote:
> > [...] Java's broken concurrency support, [...]
>
> Does anybody have a good, comprehensive list of what's broken about
> Java's concurrency support?
Here's a fairly good start (even though listing Java's
problems with concurrency didn't seem to be the focus of
the paper): "Teaching Java Concurrency to CS vs IT Students:
A Matter of Emphasis" by Charles Hughes and Marc Smith.
http://www.cs.ucf.edu/~ceh/Papers/HughesSmithPDPTA2003.pdf
Then there is of course the documentation on the JCSP page,
where a few paragraphs are dedicated to explaining why Java
needs help with concurrency:
http://www.cs.kent.ac.uk/projects/ofa/jcsp/explain.html
I quote the closing comments in that part of the document:
"We contend that concurrency is too natural a concept and
too good a tool to be set aside in system design and
implementation and that we should not be frightened to
use it to simplify both these tasks. If concurrency is
making life harder, maybe we are using the wrong kind
of concurrency ..."
Perhaps your best bet is to simply go with JCSP, if you
really need to use Java...
> Readers a bit unfamiliar with functional programming in
> general, and Erlang in particular, may want to browse through
> the source code for the widgets (it's quite brief), and note
> the nearly complete absense of error handling code.
>
> Instead, the code is written so that it will either perform
> correctly or crash. When using lots of processes, and Erlang's
> process linking concept, this means that cleanup is automatic
> and the work can either be restarted or aborted at the
> appropriate level.
>
> In the Erlang community, this is a pervasive design philosophy
> called "programming for the correct case", or "the Let it Crash
> Philosophy". It has a dramatic impact on code readability,
> especially in systems that must be very fault-tolerant and
> run non-stop (something that is, but shouldn't be, a fairly
> rare requirement.)
I never really understood this. How can you build a system to run all
the time, if you just "let it crash" in an "incorrect case"?
Haskell does pretty much have that. Look, for example, at GHC and GHCi,
although I think that some compilation of some sort is happening under
the hood in GHCi.
Monads aren't too bad; I've grown to like them. See "Using Monads" on
http://www.haskell.org/bookshelf/ for some help, if you decide to try
to tackle them.
-- Mark
Mmmm. I hear rumours that GHC may soon be quite good at using eager
evaluation much more when it can get away with it. Does anyone else
know anything about this appealing prospect?
-- Mark
I think the emphasis is not on "letting it crash", but on proper
monitoring and restarting crashed subprocesses or functions.
Altough it spares a lot of programming time, I also think that
it could be a somewhat dangerous practice, since it could hide
not only the effect pathological inputs and special cases, but
also rarely occuring bugs. Therefore, the program may remain
symptom-free while having serious internal problems impacting
e.g. performance.
I do not agree here: I think it was a very wise design choice to
support efficient handling of strings. Actually, the developers have
even refrained from putting "String.explode" and "String.implode"
(list/string-conversions) into the standard library so that people
don't even think of doing anything horribly inefficient without having
to implement their own conversion routines explicitly.
Nobody forces you to use builtin strings: use character lists if you
want. Using the preprocessor for syntactic sugaring, this could be
equally concise.
> Their imperativeness can lead to very surprising bugs. Consider this:
Imperativeness can always lead to very surprising bugs.
> Funny, yes, but functional...?
Would you prefer that the string be copied on each execution of the
function? That would be horribly inefficient!
> Haskell has all features needed to support efficient strings. It has
> unboxed arrays, unboxed mutable arrays, encapsulated local state threads
> (ST monad), etc. All it lacks are mature libraries that help using these
> features for string processing. (I know about PackedString, but I don't
> consider it mature - there is no hGetLinePS).
But you do not get around certain tradeoffs.
Regards,
Markus
> chris wrote:
> > Ulf Wiger wrote:
> >
> >>
> >> Instead, the code is written so that it will either perform
> >> correctly or crash. When using lots of processes, and Erlang's
> >> process linking concept, this means that cleanup is automatic
> >> and the work can either be restarted or aborted at the appropriate level.
> >>
> > I never really understood this. How can you build a system to run
> > all the time, if you just "let it crash" in an "incorrect case"?
>
> I think the emphasis is not on "letting it crash", but on proper
> monitoring and restarting crashed subprocesses or functions.
Correct. The phrase "Let it Crash" is intended to be somewhat
provocative, partly due to the fact that programmers not used
to "programming for the correct case" will go to great lengths
trying to protect their own process from crashing. This easily
leads to code that's riddled with tests, where many tests
detecting an error condition simply pass it on, since it's
not possible to handle the error at that level.
That sort of programming leads to a false sense of security.
You think you're writing robust code, since you're inserting
tests everywhere, when in fact, you're obfuscating the code,
hiding the important details, and introduce error-handling
code all over that is also a potential source of bugs.
> Altough it spares a lot of programming time, I also think that
> it could be a somewhat dangerous practice, since it could hide
> not only the effect pathological inputs and special cases, but
> also rarely occuring bugs.
I disagree.
- Error handling code is something that should be handled
separately, preferably by the infrastructure if possible,
or by a few highly competent programmers; it should be
rigorously tested. If you leave it up to every programmer
to test for errors and write code to handle them, this
won't be the case. Not only will they have to work harder,
they will do a poor job, threatening the integrity of the
system.
- Our experience from doing this (for 8 years now) in complex
systems, with "average" programmers, and very high reliability
requirements, is that it greatly simplifies testing and
improves quality, including pathological errors. We've
also seen that the system has a good chance of recovering
from rare errors that noone had anticipated. You may test
for known error cases if you want to handle them particularly
gracefully, but the default recovery measures will cover
a large variety of errors with a minimum of effort.
- For rarely occuring bugs, it is especially important
that the error recovery measures are generic. If you
rely on specific error recovery measures, chances are
greater that rarely occuring bugs will either slip
through the cracks (not be detected at all), or that
the error recovery code itself is buggy, since it
was most likely never tested before releasing the
product.
- With "experience", I mean millions of lines of code,
hundreds of programmers, and thousands of years of field
time with a fairly good handle of what errors have occured
in the field.
> Therefore, the program may remain symptom-free while having
> serious internal problems impacting e.g. performance.
I would not be able to prove you wrong, of course, but --
If the system performs observably well, why do I care?
Actually, with the tracing support provided by Erlang,
I seriously doubt that you are right. It is not especially
difficult to find serious performance problems. Regarding
other serious problems threatening the system's integrity,
I know of no tool or discipline that would give us better
reassurance than Erlang, given the complexity of our
products.
: I never really understood this. How can you build a system to run all
: the time, if you just "let it crash" in an "incorrect case"?
It's just like in human organisarion - when worker is unable to
continue with work at hand, he just stops and reports to manager or
supervisor.
Then it becomes supervisor responsibility to do futher actions - either
order restart work, continue with reduced capacity whatever seem to be
appropriate when seen with better view on higher level.
best regards,
taavi
--
Life would be so much easier if we could just look at the source code.
>>> In the Erlang community, this is a pervasive design philosophy
>>> called "programming for the correct case", or "the Let it Crash
>>> Philosophy".
>> I never really understood this. How can you build a system to run all
>> the time, if you just "let it crash" in an "incorrect case"?
> It's just like in human organisarion - when worker is unable to
> continue with work at hand, he just stops and reports to manager or
> supervisor.
> Then it becomes supervisor responsibility to do futher actions [...]
...kill the worker, and get a new one? :-)
-kzm
--
If I haven't seen further, it is by standing in the footprints of giants
Is it necessary to make strings mutable by default to have efficient
string processing?
You need mutable strings when you want to change a few letters in a big
string. How often do you need that?
OCaml's strings give you a limited mutability - they allow you to
change characters in the string, but you can't efficiently add
characters to the end of the string (I know about Buffer).
When I programmed in OCaml, I never really needed this limited form of
mutability. I did, however, care about efficiency, so I wouldn't use
lists. I could just use strings carefully or make a suitable abstract
data type, but it bothered me that I have to fight mutability in a
supposedly functional language.
> Actually, the developers have
> even refrained from putting "String.explode" and "String.implode"
> (list/string-conversions) into the standard library so that people
> don't even think of doing anything horribly inefficient without having
> to implement their own conversion routines explicitly.
Wouldn't it be better if such an ugly and error prone function as
String.implode was implemented once and for all?
> Nobody forces you to use builtin strings: use character lists if you
> want. Using the preprocessor for syntactic sugaring, this could be
> equally concise.
I just chose to not use OCaml. Not because of strings, I just found a
language I like more.
>> Their imperativeness can lead to very surprising bugs. Consider this:
>
> Imperativeness can always lead to very surprising bugs.
Aren't functional languages supposed to be good at avoiding mutable
state?
>> Funny, yes, but functional...?
>
> Would you prefer that the string be copied on each execution of the
> function? That would be horribly inefficient!
There would be no need to copy if strings were immutable.
>> Haskell has all features needed to support efficient strings. It has
>> unboxed arrays, unboxed mutable arrays, encapsulated local state threads
>> (ST monad), etc. All it lacks are mature libraries that help using these
>> features for string processing. (I know about PackedString, but I don't
>> consider it mature - there is no hGetLinePS).
>
> But you do not get around certain tradeoffs.
Such as?
I am quite happy with performance of programs written in Haskell. I even
think it's faster than OCaml when you program in purely functional way,
but YMMV.
> Regards,
> Markus
It should perhaps also be noted that an Erlang 'crash' is different
from a crash in an untypesafe language. I.e. in Erlang the offending
operation will flag an error and throw an exception. It's not like a
runaway pointer in 'C' that might throw the error, but the program
might also run for quite some time before the consequences of the
error become too much to bear.
Thus I'd argue that in 'C' and similar languages the "let it crash"
philosophy (which is still quite applicable) would still entail a lot
of extraneous checking code. Only that code would do no fault recovery
or fault masking, but instead complain loudly and run no further.
Being from the 'other' organisation within Ericsson that uses Erlang I
share with Ulf's experiences. Input should be checked at the boundary
to the system, and within you can/should assume it correct. The faster
the system gives up (or I should perhaps say; the higher level at
which the system gives up, since it's designed with fault tolerance in
mind) when it meets an unexpected condition, the faster the test is
going to go, and the better quality results it's going to
deliver. Otherwise, code covering for the mistakes of other code has a
tendency to delay detection and identification of the *real* culprit
and hence the fix.
P.S. That's not to say I don't prefer static type checking, but let's
not do that again shall we. :-)
That's what we do here at Ericsson... :-)
No, really, the processes that handle subscriber signalling in the
Ericsson GSN are called TC_Workers. There was a raised eyebrow at
least once where someone unfamiliar with the terminology heard the
advice: "Just kill the worker and get on with it." :-)
It seems to me that Objective CAML takes a fairly consistent
position there. It generally has the features to be good at
avoiding mutable state - like, storage that allows you to
casually construct and return objects without worrying about
who's going to recover their memory, efficient function calls
that support recursive algorithms etc.
But it rarely if ever forces you to avoid mutable state - you
can modify a string (even Python won't allow this), you can
easily choose to make other data structures mutable, you can
easily write an iterative algorithm that modifies index counters
and whatnot.
I personally am both attracted and repelled by that - attracted
mainly when I want to stop fooling around and get something done,
and repelled by the ensuing chaos. But whatever, it's good that
such an excellent implementation exists to represent a language
at this point in the spectrum of possibilities.
Donn Cave, do...@u.washington.edu
Finally something I can agree with. :)
Best regards,
Tomasz
> I am convinced that for complex concurrency, you'll want to avoid
> shared data and things like semaphores and monitors. You'll also
> want to avoid event-based programming with run-to-completion
> semantics.
I'm trying to understand the bit about event-based programming which
sounds interesting.
Do you mean
You'll also want to avoid event-based programming which we
all know has run-to-completion semantics.
or
You'll also want to avoid that kind of event-based programming
which has run-to-completion semantics.
I'm not sure what run-to-completion semantics is. If I specify
a bulk copy operation by saying that it is allowed to return
status saying "I only did some of it; call me again with these
parameters to make further progress", would that be
run-to-completion semantics?
If an operation is allowed to return a failure status, saying
"Sorry", (although eventually it mustn't), would that be
run-to-completion semantics?
I'm not 100% sure what event-based programming is, but maybe it is
connected with a that each external stimulus has its own thread which
runs an event handler. There may be all kinds of other threads, but
the basic ones are vehicles for processing events or activations of
event handlers. This seems close to reactive programming
(eg. http://www.haskell.org/frp/). It seems to be important here to
distinguish between interfaces in which the procedures come with a
hard real-time guarantee (maybe at the cost of a weak semantics), from
those where they can involve indefinite waiting, unbounded operations
etc.
Peter Hancock
> I'm trying to understand the bit about event-based programming which
> sounds interesting.
>
> Do you mean
>
> You'll also want to avoid event-based programming which we
> all know has run-to-completion semantics.
>
> or
>
> You'll also want to avoid that kind of event-based programming
> which has run-to-completion semantics.
Apparently, I need to pick a clearer definition. (:
I was refering to programming models where you have a central
event loop processing incoming messages/events in FIFO order,
dispatching a method to handle a message and waiting for it
to return control to the event loop.
This is the type of concurrency used in e.g. UML,
Most examples of event-based programming I've seen also use
this method. UML also has the wonderful default behaviour
that messages that cannot be handled in a specific state
are discarded. This introduces tricky timing dependencies,
and the risk that messages are lost if the programmer didn't
anticipate all possible state-event combinations. Very
difficult to design, and almost impossible to test.
> I'm not sure what run-to-completion semantics is. If I specify
> a bulk copy operation by saying that it is allowed to return
> status saying "I only did some of it; call me again with these
> parameters to make further progress", would that be
> run-to-completion semantics?
I guess it would. This would be a typical pattern if the
central event loop dispatches multiple state machines.
In this case, no state machine can be allowed to block for
an extended period of time, and must split long jobs into
small pieces, as you describe. This has all sorts of nasty
consequences in complex scenarios.
> If an operation is allowed to return a failure status, saying
> "Sorry", (although eventually it mustn't), would that be
> run-to-completion semantics?
Yes, if the operation returns control, and is not able to
continue autonomously.
> I'm not 100% sure what event-based programming is, but maybe it is
> connected with a that each external stimulus has its own thread which
> runs an event handler. There may be all kinds of other threads, but
> the basic ones are vehicles for processing events or activations of
> event handlers. This seems close to reactive programming
> (eg. http://www.haskell.org/frp/).
Yes, I've looked at the reactive programming thesis, and it
contains, among other things, a version of the POTS example
(Plain Old Telephony Service) from the Erlang book. It concludes
that the reactive programming version is equivalent, or even better,
which I think is a wrong conclusion. The POTS example used is
greatly simplified in some respects, and if one changes a few
things into something more realistic, the scales tip distinctly
in Erlang's favour. If one goes a bit further still, the problems
with the reactive objects model multiply. In this case, it has
to do with the inability to block while waiting for a specific
message (selective receive with implicit buffering.)
The main simplification of the program is introducing the assumption
that you can control hardware without checking the return values
(or that the hardware always responds immediately.) This is
not true in real systems; you must check return messages from the
hardware, and you should do so before handling any messages
from e.g. the network.
> It seems to be important here to
> distinguish between interfaces in which the procedures come with a
> hard real-time guarantee (maybe at the cost of a weak semantics), from
> those where they can involve indefinite waiting, unbounded operations
> etc.
Hard real-time is certainly quite different from soft real-time.
At least there could be a separate type for immutable strings. Something
along the lines of:
module type CStr = sig
type const
type mutabl
type 'a cstr
type 'a tag
val const : const tag
val mutabl : mutabl tag
val length : 'a cstr -> int
val get : 'a cstr -> int -> char
val set : mutabl cstr -> int -> char -> unit
val copy : 'a cstr -> 'b tag -> 'b cstr
val sub : 'a cstr -> int -> int -> 'b tag -> 'b cstr
val from_string : string -> 'a tag -> 'a cstr
end
module CStr = struct
type const
type mutabl
type 'a cstr = string
type 'a tag = unit
let const = ()
let mutabl = ()
let length = String.length
let get = String.get
let set = String.set
let copy s t = String.copy s
let sub s b e t = String.sub s b e
let from_string s t = String.copy s
end
...though admittedly this wouldn't be too practical to use. But in any
case, there should be either compile-time or run-time checking to make
sure that immutable strings are never modified. It's just plain evil to
allow literals to be changed.
Lauri Alanko
l...@iki.fi
> han...@spamcop.net (Peter G. Hancock) writes:
>> I'm not 100% sure what event-based programming is, but maybe it is
>> connected with a that each external stimulus has its own thread which
>> runs an event handler. There may be all kinds of other threads, but
>> the basic ones are vehicles for processing events or activations of
>> event handlers. This seems close to reactive programming
>> (eg. http://www.haskell.org/frp/).
>
> Yes, I've looked at the reactive programming thesis, and it
> contains, among other things, a version of the POTS example
> (Plain Old Telephony Service) from the Erlang book. It concludes
> that the reactive programming version is equivalent, or even better,
> which I think is a wrong conclusion. The POTS example used is
> greatly simplified in some respects, and if one changes a few
> things into something more realistic, the scales tip distinctly
> in Erlang's favour. If one goes a bit further still, the problems
> with the reactive objects model multiply. In this case, it has
> to do with the inability to block while waiting for a specific
> message (selective receive with implicit buffering.)
You seem to be talking about O'Haskell here. frp is something else.
I dunno anything about the specifics of the example you're talking about
or the technical details of POTS, but IIRC correctly avoidance of
unnecessary blocking behaviour and not allowing reactive objects to chose
what (if any) messages they got was a an important design goal of O'Haskell.
But I don't think that means an object can't wait for a specifc message. It
just means if does have to wait it doesn't have to block.
I think the standard way of doing this in O'Haskell was the "self addressed
envelope" (callback) method in the form of a closure. AFAIK closures are a
recent addition to Erlang so I suppose this technique isn't (or perhaps I
should say wasn't) an option.
I guess this leaves the question of what the reactive object should do with
any messages it gets while waiting, but this is really upto the programmer
to decide. It could for
example..
Queue them
Ignore them
Tell the sender to stop sending them (using a self addressed
envelope supplied by the sender).
Tell the sender to start sending them when it was ready (again
using a self addressed envelope supplied by the sender).
The last option above would seem more likely if waits were known to be
necessary. The sender wouldn't send any messages until it got the OK (but of
course this doesn't imply that the sender is blocked either).
Unless I'm missing something, the first (queing) option would be precisely
equivalent to a blocked process as far as the rest of the system is
concerned, if all messages (other than the one it's waiting for) were
queued. But an O'Haskell reactive object doesn't necessarily have to do
this (it could select which messages were queued).
Regards
--
Adrian Hey
I believe that's the problem. From the last time we talked about this,
the root of the question seems to be whether you need blocking I/O for
the sake of manageable state complexity. When you block, your program
doesn't have to account for the state between the I/O request and its
fulfillment, because it sleeps through it - so you can skip that state,
as it effectively doesn't exist.
The O'Haskell model doesn't allow this, so the (whole) program has more
states to account for. On the other hand, it never sleeps - it's never
blocked from responding to input. I'm not sure I followed your points
about queuing but I'm pretty sure juggling queues doesn't give you the
same simplicity as that blissful sleep of blocking I/O.
I personally find the O'Haskell model attractive the way I find functional
programming attractive. In either case, it's for someone who has more
practical experience to say, whether issues like this are inherent
weaknesses, or just problems that can be resolved fairly easily by
someone who has the acquired skills.
Donn Cave, do...@drizzle.com
>Vasile Rotaru wrote:
>
>> Not quite, you need to write simple, boring wrappers around the "pure" (what a
>> irony!) C routines to call them from the Ocaml code.
>
>SWIG can automatically generate those calls.
Not very cleanly, and the calls that were generated by OCaml
are not type safe.
OCaml would be great except for two things.
1) On a native Windows installation there are some parts missing
most notably the debugger. I could instal the cygwin version,
develope with that and compile with the Windows version, but
I would first want to upgrade my version of cygwin ( which
I have been meaning to upgrade for a year and a half, cygwin
is a pain in the ass to upgrade ).
2) OCaml has one of the ugliest FFI's I have ever seen. Yes when
I finish, I would like to call external API's through an
interface that has been tweaked io look like it has been written
in that language ( Lispers, say the interface is "Lispy" ). But
often times I initially use interfaces that I am not that
familiar. I have to do a lot of changing around of calls to the
interface. I don't want to spend the time cleaning up the
interface until I'm sure I got it right.
What's up with the Humps anyway. Lately when I click on a link the
site is dead. Like no one is maintaining the site.
The reply-to email address is olczy...@yahoo.com.
This is an address I ignore.
To reply via email, remove 2002 and change yahoo to
interaccess,
**
Thaddeus L. Olczyk, PhD
There is a difference between
*thinking* you know something,
and *knowing* you know something.
>>> - an activeX/com interface on the windows platform would be very nice...
>
>> Any language that's first implemented on Unix will acquire ActiveX/Com
>> late, and AFAIK none of the languages under discussion has it.
>
>OCaml has tools to automatically generate COM-interfaces:
>
> http://caml.inria.fr/camlidl
First generically speaking SWIG can generate COM-interfaces.
Second SWIG and corbaidl both suffer from the same fundamental
problem, corbaidl worse than SWIG.
In both cases the IDL parser is not that good.
That means you must hand translate any personal COM IDL into
the version of IDL that there two interface generators can use.
Same with CORBA.
Frankly this was a stupid thing for both groups to do, because it
is straightforward to handle COM IDL. ACE+TAO uses the
MS IDL compiler for it's front end ( as I suspect does OmniOrb ).
So it would not be hard to do the same with either SWIG or corbaidl.
I wouldn't claim that it is easy, but it is probably not much harder
than writing you own idl parser, and it will give you a much more
complete parser.
> I believe that's the problem. From the last time we talked about this,
> the root of the question seems to be whether you need blocking I/O for
> the sake of manageable state complexity. When you block, your program
> doesn't have to account for the state between the I/O request and its
> fulfillment, because it sleeps through it - so you can skip that state,
> as it effectively doesn't exist.
Sorry, that's the root of what question?
Are reactive systems (or event based systems) ones built
without blocking IO?
In the context of say Haskell where "IO" is a monad,
does blocking IO mean that we have primitives foo
foo : Inputs -> IO Results
which aren't guaranteed to ever return any result? Not having blocking
IO means that all IO primitives are must eventually return, and the
state change can be thought of as taking place in some instant between
issue and return?? As it were, all the primitives are transactions.
(The most basic thing about a transaction is that it returns -- in one
way or the other.)
I find it much easier to model interfaces which are guaranteed to
return eventually, and some single state-change occurs atomically;
than to model interfaces which may not return at all in some
circumstances. I'd prefer to believe that non-blocking interfaces are
a sufficient framework for thinking about the design of real systems.
Related to this somehow is the possibility of representing
asynchronous, buffered communication, using worker threads
for the buffers; and the possibility of implementing
"specific receive" with queues.
I came across yet another "school" of reactive programming:
http://www-sop.inria.fr/mimosa/rp/generalPresentation/index.html
They describe it as based on the notion of "instants", in which
different threads participate.
> In either case, it's for someone who has more practical
> experience to say, whether issues like this are inherent
> weaknesses, or just problems that can be resolved fairly easily
> by someone who has the acquired skills.
I think there's some kind of duality involved -- maybe even with a
classic name like the Lauer-Needham duality. Maybe if one pinned down
precisely what everyone is talking about you could just formally show
how to transform one approach into the other. My experience is that
it is futile to expect people to think in ways they find alien or
inverse to their normal way of thinking, unless you can make very
clear some good reason for doing so.
Peter Hancock
Well, supposing that they are (was that a question about terminology?),
is this a reasonably productive way to tackle the kind of complexity
you get in a real world hardware control application, or does it make
things so hard that you're tempted to cheat - write to the hardware
register and not check, for example, I don't know.
| In the context of say Haskell where "IO" is a monad,
| does blocking IO mean that we have primitives foo
|
| foo : Inputs -> IO Results
|
| which aren't guaranteed to ever return any result? Not having blocking
| IO means that all IO primitives are must eventually return, and the
| state change can be thought of as taking place in some instant between
| issue and return?? As it were, all the primitives are transactions.
| (The most basic thing about a transaction is that it returns -- in one
| way or the other.)
A non-blocking primitive doesn't just _eventually_ return, it returns
_immediately_. That's guaranteed, but I don't think it's like a
transaction. If you care whether there was actually some external
result or completion, you have to arrange for asynchronous notification
of that fact, and move on.
At the whole program level, the program presumably does actually block
for I/O, but that would be in a global dispatcher waiting for _any_ I/O.
| I find it much easier to model interfaces which are guaranteed to
| return eventually, and some single state-change occurs atomically;
| than to model interfaces which may not return at all in some
| circumstances. I'd prefer to believe that non-blocking interfaces are
| a sufficient framework for thinking about the design of real systems.
|
| Related to this somehow is the possibility of representing
| asynchronous, buffered communication, using worker threads
| for the buffers; and the possibility of implementing
| "specific receive" with queues.
|
| I came across yet another "school" of reactive programming:
|
| http://www-sop.inria.fr/mimosa/rp/generalPresentation/index.html
|
| They describe it as based on the notion of "instants", in which
| different threads participate.
| > In either case, it's for someone who has more practical
| > experience to say, whether issues like this are inherent
| > weaknesses, or just problems that can be resolved fairly easily
| > by someone who has the acquired skills.
|
| I think there's some kind of duality involved -- maybe even with a
| classic name like the Lauer-Needham duality. Maybe if one pinned down
| precisely what everyone is talking about you could just formally show
| how to transform one approach into the other. My experience is that
| it is futile to expect people to think in ways they find alien or
| inverse to their normal way of thinking, unless you can make very
| clear some good reason for doing so.
Maybe, or maybe it's just like they say - `it's easy when you know how!'
I imagine some transformation between imperative and functional is
quite feasible, and I'm sure we could describe the transformation
between blocking, internally threads and O'Haskell reactive objects.
But I'm not sure the result would be a satisfactory proof of the
viability of the latter, in either case. In fact, that's where we
came in - I think Ulf can accurately translate his idea of a valid
POTS implementation into more or less valid O'Haskell, but then he
doesn't like it because it's extremely complicated. I can only guess
there may be more than one possible translation, though perhaps at
the expense of adding some real world features to the proof of concept
thesis work we have with O'Haskell.
Donn Cave, do...@drizzle.com
> Once properly brainwashed, you will gladly contribute to the
> http://www.bluetail.com/wiki/showPage?node=RavingsAboutOop
> page on the erlang wikie. ;-) You'll find that the combination
> of lighweight processes (done properly) and functional programming
> will relieve you of any desire to use OO as anything other than
> a sometimes handy abstraction for specific problems.
What is OOP other than a handy abstraction for specific problems?
What is any pardigm other than a handy abstraction for specific problems?
If you are into CSP, then inheritance, modules, delegation, etc
are details.
Aren't functional strings something like O(log n) access and
modification? That ain't too bad...
David
> Ulf Wiger wrote:
>
> > han...@spamcop.net (Peter G. Hancock) writes:
>
> You seem to be talking about O'Haskell here. frp is something else.
Yes I was.
> I dunno anything about the specifics of the example you're talking about
> or the technical details of POTS, but IIRC correctly avoidance of
> unnecessary blocking behaviour and not allowing reactive objects to chose
> what (if any) messages they got was a an important design goal of O'Haskell.
>
> But I don't think that means an object can't wait for a specifc message. It
> just means if does have to wait it doesn't have to block.
>
> I think the standard way of doing this in O'Haskell was the "self addressed
> envelope" (callback) method in the form of a closure. AFAIK closures are a
> recent addition to Erlang so I suppose this technique isn't (or perhaps I
> should say wasn't) an option.
It can be, but it's not needed, as there is a better mechanism. ;)
> I guess this leaves the question of what the reactive object should do with
> any messages it gets while waiting, but this is really upto the programmer
> to decide. It could for
> example..
> Queue them
This is the implicit behaviour in Erlang.
> Ignore them
This is what UML does.
> Tell the sender to stop sending them (using a self addressed
> envelope supplied by the sender).
>
> Tell the sender to start sending them when it was ready (again
> using a self addressed envelope supplied by the sender).
I choose to remain sceptical until I am provided with anecdotal
evidence that this is really a good idea.
Note that I'm talking _complex_ concurrency (and concurrency problems
do get complex rather quickly...) In many (hard) real time systems,
you may have no option other than to cover all possible state-event
combinations, but this severely complicates the program.
It should also be noted that if you want to write a program in Erlang
where a process uses run-to-completion event-based semantics, it
is simple enough:
main_loop(Module, State) ->
receive
{Msg, Data} ->
S1 = Module:Msg(Data, State),
main_loop(S1)
end.
The above program will always pick the first message in the queue
and use the first attribute in the message as a basis for method
dispatch (Msg could be e.g. 'read', 'write', ..., which would
correspond methods with the same name in the module Module.)
This would constitute something similar to reactive programming
in Erlang. In the Erlang/OTP library, you will find that e.g.
the gen_server and gen_fsm behaviours work this way.
I once wrote some example programs in order to compare different
models of handling concurrency, and just recently put them up on
a web page accessible from outside Ericsson:
http://www.wiger.net/uffe/erlang/plain_fsm0.4/doc/pots/index.html
It tries to illustrate what happens with non-blocking semantics
in cases where the sender really does need some form of reply, and
especially when you have several such interfaces that start
interfering with each other.
> Unless I'm missing something, the first (queing) option would be precisely
> equivalent to a blocked process as far as the rest of the system is
> concerned, if all messages (other than the one it's waiting for) were
> queued.
Why would it be? The process may be queueing messages for a while, and
this may block the sender (assuming it is waiting in a selective
receive, implicitly queueing messages in its turn.) It will not block
processes not involved in the conversation.
>But an O'Haskell reactive object doesn't necessarily have to do
> this (it could select which messages were queued).
See above. Using Erlang's semantics, you can choose whether you
want your program to block or be fully asynchronous. What it
doesn't give you is the possibility of statically analysing the
"reactive properties" of your program.
Regards,
> Ulf Wiger <ulf....@CUT-ericsson.com> wrote in message news:<xcz8yji...@CUT-ericsson.com>...
>
> > Once properly brainwashed, you will gladly contribute to the
> > http://www.bluetail.com/wiki/showPage?node=RavingsAboutOop
> > page on the erlang wikie. ;-) You'll find that the combination
> > of lighweight processes (done properly) and functional programming
> > will relieve you of any desire to use OO as anything other than
> > a sometimes handy abstraction for specific problems.
>
> What is OOP other than a handy abstraction for specific problems?
To many, it is _the_ abstraction to be applied to all problems. ;)
(Perhaps not a commonly held view here in comp.lang.functional...)
I've seen it on numerous occasions that people learning e.g.
Erlang have great difficulty programming in a non-OO language,
and I've come across many who fail to see e.g. Erlang as an
interesting tool simply because it isn't OO. It is in fact so
common that I would say it happens more often than not.
/Uffe
If I implemented strings this way, programs that perform heavy string
processing would slow down by at least an order of magnitude and use an
order of magnitude more space. Constants do matter in practice...
A good implementation would would incur only small constants.
For example, it could store the characters in arrays and define the
String type as a list of array-offset-length tuples.
When heavy string processing (i.e. heavy string construction) is
involved, the program can construct a "normalized" version of a String
value that's constructed of a single tuple.
I see no reason why this wouldn't give you performance in the same class
as that of C, *particularly* if heavy string processing is involved
(since C has to do all kinds of ugly memory allocation stuff to store
intermediate results, something that functional languages are better
optimized for).
Ironically, C is faster if there's no construction of new string values.
I don't think that the overhead for that case is an entire order of
magnitude though.
The Boehm-Demers-Weiser collector comes with an immutable string
library, so the above isn't "just theory".
Regards,
Jo
--
Currently looking for a new job.
Did you mean to say a _tree_ of array-offset-length tuples?
(If not, wouldn't concatenation be worse than O(log n)?)
--
Fergus Henderson <f...@cs.mu.oz.au> | "I have always known that the pursuit
The University of Melbourne | of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
> Joachim Durchholz <joachim....@web.de> writes:
>
>> For example, it could store the characters in arrays and define the
>> String type as a list of array-offset-length tuples.
>
> Did you mean to say a _tree_ of array-offset-length tuples?
Yes.
It was too early... I meant to type "series", and the terminology all
got mixed up - time to go to bed now :-)