Google Groups Home
Help | Sign in
Message from discussion F#
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Jon Harrop  
View profile  
 More options Nov 4 2007, 12:27 am
From: Jon Harrop <j...@ffconsultancy.com>
Date: Sun, 4 Nov 2007 04:27:04 +0000
Local: Sun, Nov 4 2007 12:27 am
Subject: Re: F#
On Saturday 03 November 2007 14:36, Steve Lianoglou wrote:

> Hi Jon,

> > OCaml is much less
> > interesting for web programming and much more interesting for technical
> > users like scientists. I believe great things could be done with an ML
> > targetting the JVM.

> I believe you're actively looking at/working with scala as well, so
> I'm just wondering what (in your opinion) an OCaml-on-the-jvm
> implementation would add that can't currently be addressed by Scala
> (except for being able to run real/already-written Ocaml stuff on the
> JVM).

> In other words, what's scala missing that has you pining for OCaml?

> Just curious (from a person who hasn't done much work in either),

OCaml and F# have shown that ML's approach to structured programming using
modules, variant types and pattern matching and extensive type inference is
almost always preferable to OOP. When given the choice between OOP and FP,
seasoned programmers rarely choose OOP.

However, Scala's design ignores many of the lessons learned over the past 35
years from the ML family of languages and, instead, panders to OOP and tries
to integrate OOP with pattern matching. While this might be of academic
interest, it is of little practical relevance because the resulting language
fails to capture the benefits of ML (let alone the benefits added by OCaml
and F#).

OCaml programmers are typically over 10x as productive as Java or C++
programmers for a wide range of practical tasks. Despite being based upon a
fundamentally OOP platform, F# goes a long way to capturing the productivity-
boosting benefits of OCaml (and the whole ML family). In contrast, Scala
fails to capture many of the benefits including some really basic ones and,
consequently, writing correct code is much more difficult in Scala than in
any real ML.

Moreover, the ML family of languages are designed to be succinct but Scala is
needlessly verbose for everything from "Hello world!" upwards. The ML family
of languages provide extensive type inference (OCaml more than most) but
Scala has only rudimentary inference by comparison. OCaml has an unusually
expressive type system but Scala adds little to OOP that is of practical
importance.

For example, here is "Hello world!" in F#:

  printf "Hello world!"

and in Scala:

  object HelloWorld {
    def main(args: Array[String]) {
      println("Hello world!")
    }
  }

Here is a simple 5-line derivative function in OCaml:

# let rec d f x = match f with
    | `Var y when x=y -> `Int 1
    | `Int _ | `Var _ -> `Int 0
    | `Add(f, g) -> `Add(d f x, d g x)
    | `Mul(f, g) -> `Add(`Mul(f, d g x), `Mul(g, d f x));;
val d :
  ([< `Add of 'a * 'a | `Int of 'b | `Mul of 'a * 'a | `Var of 'c ] as 'a) ->
  'c -> ([> `Add of 'd * 'd | `Int of int | `Mul of 'a * 'd ] as 'd) = <fun>

OCaml inferred the sum type and checks the pattern match for exhaustiveness
and redundancy. Type inference has even proven that the `Var construct will
never survive an application of this derivative function "d".

Scala cannot infer the sum type used to represent a symbolic expression so you
must start by declaring it explicitly:

  abstract class Expr
  case class Lit(f: Int) extends Expr
  case class Var[T](x: T) extends Expr
  case class Add(f: Expr, g: Expr) extends Expr
  case class Mul(f: Expr, g: Expr) extends Expr

Note that the declaration of the type is as big as the entire OCaml solution.
This declaration contains numerous superfluous keywords such
as "abstract", "case", "class", "extends" and the name of the ABC "Expr".

In Scala, the derivative function becomes another 6 lines of code:

  def d[T](f: Expr)(x: T): Expr = f match {
    case Var(y) => if (x==y) (Lit(1)) else (Lit(0))
    case Lit(_) => Lit(0)
    case Add(f, g) => Add(d(f)(x), d(g)(x))
    case Mul(f, g) => Add(Mul(f, d(g)(x)), Mul(g, d(f)(x)))
  }

The types of the arguments must be explicitly declared. You can sometimes omit
the return type but in this case Scala is unable to infer it so you must also
declare that explicitly.

Static checking for exhaustiveness and redundancy of such pattern matches is a
critical feature in all other modern FPLs. OCaml is particularly clever is
this respect:

# function
    | 0 -> true
    | 1 -> false;;
Warning P: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
2
- : int -> bool = <fun>

Note how OCaml not only warns you that the pattern match is not exhaustive but
even gives you an example of a value that is not handled.

This static checking becomes invaluable as more sophisticated pattern matches
are used. Consider:

# let foo = function
    | true, _ -> 0
    | _, false -> 1;;
Warning P: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
(false, true)
val foo : bool * bool -> int = <fun>

OCaml correctly identifies the only value that is not handled. In constrast,
Scala provides no such assurance, no warning, and just silently compiles this
erroneous code:

scala> def foo(b: (Boolean, Boolean)): Int = b match {
     |   case (true, _) => 0
     |   case (_, false) => 0
     | }
foo: ((Boolean, Boolean))Int

scala> foo(false, true)
scala.MatchError: (false,true)
   at .foo(<console>:4)
   at .<init>(<console>:5)
   at .<clinit>(<console>)
   at java.lang.Class.initializeClass(libgcj.so.81)
   at RequestResult$.<init>(<console>:3)
   at RequestResult$.<clinit>(<console>)
   at java.lang.Class.initializeClass(libgcj.so.81)
   at RequestResult$result(<console>)
   at java.lang.reflect.Method.invoke(libgcj.so.81)
  ...

With the latest version of Scala you can add "sealed" to your abstract class
to declare that it is a closed sum type (like an ordinary ML variant):

object E {
  sealed abstract class Expr
  case class Lit(f: Int) extends Expr
  case class Var[T](x: T) extends Expr
  case class Add(f: Expr, g: Expr) extends Expr
  case class Mul(f: Expr, g: Expr) extends Expr

}

in which case the Scala compiler will try to check patterns for exhaustiveness
of redundancy but this is only available for sum types and not all types as
all MLs do.

So, while Scala is unquestionably an improvement over Java, I do not consider
it to be a front-runner among modern functional programming languages.

--
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/products/?e


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.

Create a group - Google Groups - Google Home - Terms of Service - Privacy Policy
©2009 Google