Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Haskell --- Scheme --- ML

38 views
Skip to first unread message

Wesley T Perkins

unread,
Aug 25, 2000, 3:00:00 AM8/25/00
to
I would like to know in a nutshell the mutually distinguishing features of
Haskell, Scheme, and ML. Both aesthetic and pragmatic viewpoints are most
welcome.

I ask this not as a language bigot, but as someone who never met a language
he didn't love, other than PL1 and maybe VB.

Thanks,
Wesley


Marshall Abrams

unread,
Aug 31, 2000, 5:18:04 PM8/31/00
to
In article <8o6kkd$msa$1...@cantaloupe.srv.cs.cmu.edu>,

Wesley T Perkins <wes...@starbath.com> wrote:
>I would like to know in a nutshell the mutually distinguishing features of
>Haskell, Scheme, and ML. Both aesthetic and pragmatic viewpoints are most
>welcome.

Of course doing the question justice "in a nutshell" would be
difficult and lengthy. Here are some points that are prominent
in my mind and which seem like things some people would care
about. These are only my opinions; others may not agree with my
spin on things. Also, most of my experience with Scheme and ML
predates the latest language standards, so I could be wrong
about something here as a result.

Scheme is more freewheeling. You have a lot of flexibility, but
on the other hand you have to "roll your own" more often. Well,
then again it depends:

Scheme: dynamically typed; ML, Haskell: statically typed.

That is, ML and Haskell make you work harder up front in cases
where you need, say, a list that will in effect hold different
kinds of things. Because you've got to define a new datatype
that encompasses those different things. So in this sense, you
have to "roll your own" more in ML/Haskell, because Scheme will
just let you use the built-in lists with doing anything extra.
(But in Scheme you might not find out about certain bugs until
you run the program and you try to pass the wrong kind of thing
to a function that doesn't know what to do with it.) The nice
thing about ML and Haskell, though, is that though they're
statically typed, you're not required to attend to all of the
nitpicky detail in your declarations that C or Java, for
example, require.

On the other hand, ML and Haskell have more built-in support for
defining your own data types and data structures.

On the other hand again, it's often possible to get a Scheme
library that already adds a version of whatever feature it is
you want.

ML and Haskell have very convenient pattern-matching facilities
that let you easily assign different parts of a data structure
to different variables.

Haskell has some very nice high-level conveniences that I think
ML lacks. The one I'm thinking of is list comprehensions, which
let you define a list from functions in a way that's similar to
a certain kind of set definition in mathematics.

Haskell is "pure"; it doesn't allow you to change the value of
a variable once it's set--not noway, not nohow. However, there
is a way to simulate this sort of thing, called "monadic" programing.

In Haskell you're forced to deal with monadic programming if you
want to do much with IO. Monadic programming has one syntax
that makes it look like traditional imperative programming, but
if you go a little deeper, it's somewhat confusing if you ask
me. I think ML IO is probably more natual. Scheme IO certainly
is.

Haskell is "nonstrict". That is, the argument to a function is
often not evaluated until necessary. This is a nice thing in
some ways, but there are different opinions about the merits of
nonstrictness and what it's performance implications are. I'm
coming to appreciate it more than I used to. Aside from the
fact that it lets you define infinite lists, which some people
like, there are cases where it leads to greater efficiency. I
used to wonder whether this would really matter that much in
practice, but I recently read a discussion of a particular
performance problem in Java which would not be an issue at all
given nonstrictness--there would be no problem to work around.
Nonstrictness also means that you can easily define certain
types of operations that would require a special kind of
definition facility like Scheme's macros. For example if you
didn't have an 'and' operator that only evaluated its second
argument when the first is true, this would be easy to define
in Haskell.

There are relatively easy ways to get the effect of delayed
evaluation of arguments in Scheme and ML, though it's not as
convenient as in Haskell. And in Haskell you can force certain
function parameters to be strict if necessary.

ML and and Haskell have module/package systems. I think ML's is
supposed to be a lot fancier, but I'm not familiar with the
current module system. I suspect that the extra complication is
nice if you have a large project which can benefit from it, but
that it makes the module system harder to understand at first.

In Scheme, every function has prefix syntax, there are no infix
operators, and every function call is surrounded by
parentheses. There are lots of parentheses--this bothers some
people, but I think it's not hard to get used to if you use an
editor that will match parentheses and help you indent
properly. Or you can get used to indenting properly by hand.
But the parentheses matching is essential. And since the syntax
for code is the same as that for lists, in fact code starts out
as lists, it's easy to write programs that construct programs on
the fly. This may be a good or bad thing depending on the
context.

Haskell and ML have a syntax for curried functions--functions
which return functions--which makes partial evaluation
convenient. That is, it's common to define a function that you
think of as taking, say, two arguments. But then you might
sometimes just pass the first argument, and get back a more
specific function which takes only one argument. You can do
similar things in Scheme, but the syntax is not as nice.

There are differences between Standard ML and CAML. Most
prominent in my mind is that the newest version of the latter,
Objective Caml, has a relatively mainstream object oriented
system added to it. Haskell actually includes some of the
concepts that are associated with object oriented programming,
but in a very different form. As I recall, something similar
could be said of Standard ML modules maybe, though they've
changed since I spent a lot of time with SML. Some Scheme
implementations have built-in object systems, and in any event
there are many libraries which add object oriented features to
Scheme. It's easy to define object facilities in Scheme.
(Scheme was actually invented partly for the purpose of doing
research on object oriented programing when that approach was
still relatively new.)

Haskell is more flexible than ML about how it deals with
different type of numbers. While you don't get completely
automatic coercion from say, integers to floats or that sort of
thing, as you would in Scheme, you can often pretend that you
do, and Haskell will figure out what numeric type is
appropriate. Though you can of course tell it what's
appropriate if you don't want to leave it up to Haskell. I
think that Standard ML is less convenient, but I may be
wrong--it's been a while. Objective Caml, at least, is less
convenient, making you use different operator names for integer
addition and floating point addition, for example.

There are many more Scheme implementations than ML or Haskell
implementations. This probably has to do as much with the fact
that it's easier to implement Scheme as that it's maybe more
popular. I could be wrong about either point, though. But
there are popular implementations of the other language which
seem to be very good and have a lot of support.

Scheme has been around the longest, next ML, then Haskell. Some
people think of Haskell as more cutting edge, or bleeding edge.
Certainly nonstrictness, though not really a new idea, is a new
idea for most programmers.

One last opinion: Not to knock ML, but I personally think
Scheme and Haskell are very beautiful languages which just
feel good to work with.
--
Marshall Abrams ab...@midway.uchicago.edu

Greg Michaelson

unread,
Aug 31, 2000, 5:19:41 PM8/31/00
to
Wesley T Perkins (wes...@starbath.com) wrote:
: I would like to know in a nutshell the mutually distinguishing features of
: Haskell, Scheme, and ML. Both aesthetic and pragmatic viewpoints are most
: welcome.

|type discipline |evaluation|state |aesthetics
--------+-----------------------+----------+----------------+-------------
Haskell |parametric polymorphism|lazy |pure declarative|too many
| | | |features
--------+-----------------------+----------+----------------+-------------
Scheme |ad-hoc polymorphism |strict |more imperative |too many
| | |than declarative|brackets
--------+-----------------------+----------+----------------+-------------
ML |parametric polymorphism|strict |more declarative|too many
| | |than imperative |reserved words

Greg Michaelson


Albert Y. C. Lai

unread,
Aug 31, 2000, 5:18:49 PM8/31/00
to
"Wesley T Perkins" <wes...@starbath.com> writes:

> I would like to know in a nutshell the mutually distinguishing features of
> Haskell, Scheme, and ML. Both aesthetic and pragmatic viewpoints are most
> welcome.

A few off the top of my head:

Distinguishing Scheme from {ML, Haskell}:

- dynamic typing

- no algebraic data type

- prefix notation with compulsory parentheses

- this one I am not sure, please correct me: currying is not convenient
or natural

Distinguishing ML from Haskell:

- strict and eager evaluation

- impure

- a sophisticated, expressive, and polymorphic module system

- no "type classes" or constrained polymorphism

- no operator/identifier overloading

Do not read points in the form "no such and such" as disadvantages;
they are probably unnecessary because of some other points, e.g., ML
does not have type classes or overloading, but its polymorphic module
system more or less makes up for them.

> I ask this not as a language bigot,

It is sad that the default assumption is "you are a fanatic", and you
are required to swear that you are not one.

I am a fanatic for static typing, however, and I am slowly becoming a
fanatic for Haskell, too. :)

Arthur H. Gold

unread,
Aug 31, 2000, 5:17:48 PM8/31/00
to
Wesley T Perkins wrote:
>
> I would like to know in a nutshell the mutually distinguishing features of
> Haskell, Scheme, and ML. Both aesthetic and pragmatic viewpoints are most
> welcome.
>
> I ask this not as a language bigot, but as someone who never met a language
> he didn't love, other than PL1 and maybe VB.
>
> Thanks,
> Wesley
Though I lack a handy nutshell in which to package my
answer, here goes:

Scheme is a dynamically-typed, strict, mostly functional
language (though you can easily do imperative style
programming, including side-effects). Parameter passing is
by value. It also supports first-class continuations and
closures, and has (as of R5RS) a hygienic macro system
(which is made useful, in part, due to its extremely simple
syntax wherein all code _looks like_ lists, which is its
basic data type). Though functional in nature, Scheme is a
great language to use as a research testbench; you can
simulate just about possible programming language contruct
in it, without even breaking a sweat.

ML is also strict; unlike Scheme, it's strongly typed. It
really has no facility for imperative programming, but
side-effects can be achieved by using ref types.

Haskell is a pure, lazy functional language. There are no
side effects, but imperative-style programming can be
achived through the use of monads. It also supports
currying.

In all cases functions are first-class (can be passed as
parameters)--and the use of higher-order functions (those
that take functions as parameters) to handle stereotypical
control structures is invaluable for each. As Erik Meijer
has said: "General recursion is the `goto' of functional
languages."

Both ML and Haskell's syntax are governed by layout (unlike
Scheme, where everything is determined by all those damned
parentheses--which fortunately more or less disappear from
view once you get used to them).

Oh yeah...all are garbage collected.

I would encourage you to look at

http://www.schemers.org
http://www.haskell.org
http://cm.bell-labs.com/cm/cs/what/smlnj/index.html

for more information (that nutshell is getting pretty full).

disclaimer: I'm not an ML-er, so double-check anything I've
said about it (it's not that I'm against it, I just haven't
gotten there yet...don't flame me! ;-)

HTH,
--ag

--
Artie Gold, Austin, TX (finger the cs.utexas.edu account
for more info)
mailto:ag...@bga.com or mailto:ag...@cs.utexas.edu
--
"I'd sooner fly another combat mission than ride the Cyclone
again" -- Joseph Heller

Mark Seaborn

unread,
Aug 31, 2000, 5:18:22 PM8/31/00
to
"Wesley T Perkins" <wes...@starbath.com> writes:

> I would like to know in a nutshell the mutually distinguishing
> features of Haskell, Scheme, and ML. Both aesthetic and pragmatic
> viewpoints are most welcome.
>
> I ask this not as a language bigot, but as someone who never met a
> language he didn't love, other than PL1 and maybe VB.

Haskell is purely functional and doesn't provide any imperative
features (although you can emulate them using monads). ML and Scheme
have imperative features, but in Scheme everything is mutable, whereas
in ML you must declare what data structures are mutable.

Haskell uses lazy evaluation. ML and Scheme are strict languages
(that is, arguments are evaluated before being passed to a function),
but you can emulate laziness in them.

Scheme is dynamically typed. Haskell and ML are statically typed.

Scheme is a member of the LISP family of languages and so programs are
(syntactically) built out of S-expressions; there are no keywords as
such. ML and Haskell have more complex grammars. Scheme has macros;
ML and Haskell don't.

ML has a parametric module system (modules can be passed as parameters
to functors to yield new modules). I don't think Haskell has
parametric modules; it has `type classes' instead (which are similar
pragmatically to parametric modules, but more is inferred
automatically). The Scheme standard, R5RS, doesn't specify a module
system, but Scheme's flexibility makes it easier to build one.

Scheme has fewer widely-accepted libraries (and R5RS is a minimal
standard), whereas Standard ML and Haskell have bigger standard
libraries.

Roughly speaking, Scheme is older than ML, which is older than
Haskell. And correspondingly, Scheme has more implementations than
ML, which has more implementations than Haskell.

--
Mark Seaborn
- msea...@bigfoot.com - http://members.xoom.com/mseaborn/ -

``I couldn't do this for a living, even if they paid me''
-- Tomorrow's World presenter

Rob Warnock

unread,
Sep 1, 2000, 10:06:27 AM9/1/00
to
Mark Seaborn <msea...@argonet.co.uk> wrote:
+---------------

| Scheme is a member of the LISP family of languages and so programs are
| (syntactically) built out of S-expressions; there are no keywords as such.
+---------------

Minor quibble: R5RS Scheme *does* have a few syntactic "keywords"
(primitive expressions): quote, quasiquote, set!, if, lambda, define,
define-syntax, let-syntax, & letrec-syntax [plus constants, variables, and
procedure call, which are primitive syntax but don't involve "keywords"].

But, yes, all of the other Lispy "keywords" (let, letrec, let*, case, cond,
and, or, &c.) can be derived from the primitive expressions with macros.


-Rob

-----
Rob Warnock, 41L-955 rp...@sgi.com
Applied Networking http://reality.sgi.com/rpw3/
Silicon Graphics, Inc. Phone: 650-933-1673
1600 Amphitheatre Pkwy. PP-ASEL-IA
Mountain View, CA 94043

Rob Warnock

unread,
Sep 1, 2000, 10:06:53 AM9/1/00
to
Albert Y. C. Lai <tre...@vex.net> wrote:
+---------------

| Distinguishing Scheme from {ML, Haskell}:
...

| - this one I am not sure, please correct me: currying is not convenient
| or natural
+---------------

Well, I usually find it quite convenient enough for my simpleminded purposes:

> (define (curry-left f . args)
(lambda x (apply f (append args x))))
> (define (curry-right f . args)
(lambda x (apply f (append x args))))
> (map (curry-right / 2) '(0 1 2 3 4 5 6))
(0 1/2 1 3/2 2 5/2 3)
> (map (curry-left / 2) '(1 2 3 4 5 6))
(2 1 2/3 1/2 2/5 1/3)
> (define debug (curry-left print* "DEBUG: "))
> (debug '(+ 2 3) " = " (+ 2 3))
DEBUG: (+ 2 3) = 5

Christopher Browne

unread,
Sep 1, 2000, 10:05:42 AM9/1/00
to
Centuries ago, Nostradamus foresaw a time when Arthur H. Gold would say:

>Wesley T Perkins wrote:
>ML is also strict; unlike Scheme, it's strongly typed. It
>really has no facility for imperative programming, but
>side-effects can be achieved by using ref types.

Scheme "is too" strongly-typed; the difference is that of
_static_ typing versus dynamic typing.
--
cbbr...@ntlug.org - <http://www.ntlug.org/~cbbrowne/linux.html>
"Jedes grosse Unternehmen braucht sein Vietnam, und Microsoft wird
seines mit NT erleben" (Each large company needs its Vietnam, and
Microsoft will experience it with NT...) -- Irving Wladwasky-Berger
(IBM-Vice-President)

John Prevost

unread,
Sep 2, 2000, 8:47:20 PM9/2/00
to
>>>>> "rw" == Rob Warnock <rp...@rigden.engr.sgi.com> writes:

rw> Mark Seaborn <msea...@argonet.co.uk> wrote: +---------------
rw> | Scheme is a member of the LISP family of languages and so
rw> programs are | (syntactically) built out of S-expressions;
rw> there are no keywords as such. +---------------

rw> Minor quibble: R5RS Scheme *does* have a few syntactic
rw> "keywords" (primitive expressions): quote, quasiquote, set!,
rw> if, lambda, define, define-syntax, let-syntax, & letrec-syntax
rw> [plus constants, variables, and procedure call, which are
rw> primitive syntax but don't involve "keywords"].

rw> But, yes, all of the other Lispy "keywords" (let, letrec,
rw> let*, case, cond, and, or, &c.) can be derived from the
rw> primitive expressions with macros.

A quibble with your quibble. "keywords" generally refers to words
which cannot be bound to variables. Even the special forms of Scheme
and Lisp can be rebound:

guile> (define (if x y) (+ x y))
guile> (if 1 2)
3

This is the way that they're not keywords.

John.

Will Clinger - Sun Microsystems

unread,
Sep 5, 2000, 6:54:00 PM9/5/00
to
Jeremy H. Brown wrote:
> I've been implementing a scheme compiler for my own nefarious
> purposes.

Are there any other kind?

> When implementing macro expansion, I ran into the appended
> problem; I was unable to satisfy myself as to what the R5 spec would
> demand in the situation.

FWIW, I agree with you that

(letrec-syntax
((foo (syntax-rules () ((foo) bar)))
(bar (syntax-rules () ((bar) baz))))
(((foo)) 1 3 5 7))

is an error.

> Since people are on the topic of R5 macros
> anyhow, I figured I'd throw it out and see if there's any consensus on
> what "should" happen, either according to the standard, or according
> to good taste.

Can't help you there, but according to _my_ taste the implementation
should at least warn of the error. (Implementation-specific joke:
provided of course that the (ISSUE-WARNINGS) parameter is true.)

Will

Algometric Petrofsky

unread,
Sep 7, 2000, 1:33:54 AM9/7/00
to
I say you are correct. The form

(letrec-syntax
((foo (syntax-rules () ((foo) bar)))
(bar (syntax-rules () ((bar) baz))))
(((foo)) 1 3 5 7))

is not a valid expression. To take a simpler example,

(letrec-syntax
((foo (syntax-rules () ((foo) bar)))

(bar (syntax-rules () ((bar) #t))))
((foo)))

is not a valid expression, because ((foo)) is not a valid expression
in the environment in which it appears. We know ((foo)) is not a
program-defined expression because r5rs section 4.3 says that a
program-defined expression is always a list whose first element is an
identifier. The procedure call is the only expression type in all of
section 4 (Expressions) that can be a list whose first element is a
list. The operand of a procedure call must be an expression. (foo)
is a valid macro use insofar as it expands to bar, but bar is not a
valid expression at this location because there is no variable binding
for bar (even if there is one at the top level, it is here being
occluded by the lexical binding).

Does that make you feel any more certain?

-al

Michael Sperber [Mr. Preprocessor]

unread,
Sep 8, 2000, 10:17:14 AM9/8/00
to
>>>>> "Christopher" == Christopher Browne <cbbr...@knuth.brownes.org> writes:

Christopher> Centuries ago, Nostradamus foresaw a time when Arthur H. Gold would say:


>> Wesley T Perkins wrote:
>> ML is also strict; unlike Scheme, it's strongly typed. It
>> really has no facility for imperative programming, but
>> side-effects can be achieved by using ref types.

Christopher> Scheme "is too" strongly-typed; the difference is that of
Christopher> _static_ typing versus dynamic typing.

Well, that depends on your definition of "strongly-typed." If you
mean that Scheme will always tell you when a type error happens during
execution, you're wrong. See Section 1.3.3:

"It is an error for an operation to be presented with an argument that
it is not specified to handle."

Section 1.3.2 specifies what this means:

"If such wording [`an error is signalled'] does not appear in the
discussion of an error, then implementations are not required to
detect or report the error, though they are encouraged to do so."

It's just that most (all?) implementations of Scheme usually run in a
mode that performs type checking.

--
Cheers =8-} Mike
Friede, Völkerverständigung und überhaupt blabla

0 new messages