But this doesn't imply than the language compared is worse than merd. There are of course different choices and your mileage may vary. As Matz is saying:
Why should you switch to Ruby? If you are happy with Perl or Python, you don't have to. But if you do feel there must be a better language, Ruby may be your language of choice. Learning a new language is harmless. It gives you new ideas and insights. You don't have to switch, just learn and try it. You may find yourself comfortable enough with Ruby to decide to switch to it.You can also have a look at merd's Known Flaws.
Perl's diagnose messages in case of problems are great. They usually suggest how to fix the problem. merd will try to follow this trend. As an example, merd could suggest the use "xxx!" instead of "xxx" when the return value is ignored and "xxx!" exists.
Perl's "local", Scheme's "fluid-let" and Haskell's implicit variables allows to set a variable temporarily. This is easily introduced is merd. This construct replaces the saving of the old value and its restoration (with taking care of exceptions!)
merd doesn't have Perl's inability to local'ize local (lexical) variables
in merd, "or" doesn't work on booleans the way it does in perl. But merd's "or" is used alike perl's.
In "a or b", instead of testing the truth value of "a", it catches any exception "a" raise. If no exception is raised, "a" is returned. Otherwise, the exception is caught and "b" is executed.
region-based inference permits this on non-incremental garbage collectors
push, pop, indexing, adding... available
In Perl and Python, there is no character value. The functions that usually work on characters like "ord(c)" await a string of length 1.
In merd, Builtin::Char is a subtype of Builtin::String. It means you can use a character where a string is needed, but not the contrary.
merd uses type inference to have the expressivity of a dynamically typed language together with the static checks of statically typed languages.
"==" and "+" works on many types
auto-flattening makes complicate data strutures harder to use/think-about
but still expressive thanks to:
regexps are statically type checked in merd. There is no $1 global variables
in Perl, you need to do "split //, $string" to get a list of characters ; whereas in merd you can use "each" directly on a string. Just like in Perl, you can't directly index a string, this being a bad habit. In both Perl and merd strings are immutable.
merd adopts the Python feature for referencing intra-packages.
In Perl and Python, there is no character value. The functions that usually work on characters like "ord(c)" await a string of length 1.
In merd, Builtin::Char is a subtype of Builtin::String. It means you can use a character where a string is needed, but not the contrary.
Why? because in Python:
Python does close to no checks. PyChecker can only find local, mostly syntaxical errors.
merd also provides data sharing using inoutness inference
python is not consistent: it does not allow easy sharing of its builtin types
(merd could have some using some "Unsafe" tag)
especially nice for the specialization of "+", "=="
merd uses type inference to have the expressivity of a dynamically typed language together with the static checks of statically typed languages.
in Python, you can both iterate and index a string. In merd, you can use "each" to iterate on a string, but you can't index a string. In both Perl and merd strings are immutable.
in OCaml, "length" is either "Array.length" or "List.length" or "String.length". You have to choose which one you want. This limits polymorphism.
Another OCaml syntax weirdness is "+." which works on floats.
This feature may seems like a gadget, but script programming use it very often
When you are using a list, it doesn't mean you don't need indexing. Conversely you sometimes need to remove some elements in an array. Scripting languages (perl...) have blurred lists and arrays successfully (in term of expressivity). In merd, you can switch easily from an list implementation to an array implementation. One could even dream of an optimizing compiler which would make the choice on its own... (about this idea in Haskell)
OCaml tries hard to detect everything at compile-time, and if it can't, it signals at runtime... except for numerical operations where overflowing is simply ignored. In merd, the default is to use bignums and bigrats which can't overflow (nor underflow). And for Int's which can overflow, merd will raise an exception when it occurs. There may be a flag to disable this verification for the sake of speed, at the price of safeness (just like OCaml's "-unsafe" option)
The various constructs in OCaml permits to rarely use explicit block delimiters. The drawback occurs when something is wrong: the syntax richness tends to get in the way, especially when programming imperatively.
Since OCaml doesn't restrict the polymorphic variants expressivity, the syntax error on big variants easily gets unreadable. merd workarounds this by reducing the expressivity of polymorphic variants (every value being a polymorphic variant in merd): when a constructor is declared as belonging to a type, it can't be anonymous anymore. In practice, it means you must either not declare any type, or declare every type for the various constructors.
A second restriction occurs for undeclared constructors: they can't be applied to different types. To allow this, the types must be given.
The goal of these restrictions is to enable the full power of polymorphic variants without the bleeding edge.
OCaml has both records and objects. The records do not allow subtyping. The object's value is a record, but is only accessible in the object's methods. This means using an object for having record subtyping is quite heavy-weight (since you need to use accessors for reading and writing), but is possible.
This means you can't pass a compatible object to a function expecting an ancestor ; you must down-cast to that ancestor first. For small functions, this is not a big deal, since when unspecified, the type inference doesn't use the name of the objects. Instead it keeps the name&types of the method used. You get an open type and a kind of subsumption.
But there are case when things are not that nice. A typical example is to make a list of different objects having a common ancestor. To create such alist you have to down-cast to that common ancestor.
You can't write "map Some l", you must eta-expand "Some" first: "map (fun e -> Some e) l"
ML partial application mix up two different beasts:
There is also the monomorphism restriction:
let g1 f = f 1, f "1";; let g2 = let doubles = List.map (fun e -> e,e) in doubles [1], doubles ["1"];;
g1 and g2 are ill-typed. g1 can't be expressed in OCaml, and g2 must be written:
let g2 = let doubles l = List.map (fun e -> e,e) l in doubles [1], doubles ["1"];;
"filter (> 1) list" is illegal in OCaml. "filter ((>) 1) list" is allowed, but it's unreadable, since "(>) 1" means "fun x -> 1 > x" and not "fun x -> x > 1"
merd allows "(> 1)" which is "x -> x > 1" as expected (Haskell has the same feature)
The type of "fst(x,_) = x" is "x, _ -> x" instead of "a, b -> a" in Haskell and "'a * 'b -> 'a" in OCaml.
This may be get longer types, but these types are more readable.
Monads are a great thing when you want a tidy/explicit handling of the state. But of course one needs to think things differently. A simple example is:
* merd *********************************
do_with_env(doit,l,env,f) =
r_env = env
l' = l.doit(e -> (e', r_env) = f(e, r_env) ; e')
l', r_env
* ocaml ********************************
let do_withenv doit f env l =
let r_env = ref env in
let l' = doit (fun e ->
let e', env' = f !r_env e in
r_env := env' ; e'
) l in
l', !r_env
This can't be done in haskell without introducing state handling in "doit". So disallowing state reduce expressivity. The advantage gain for haskell comes from lazy evaluation, which notably simplifies iterators. For some kind of programs, loosing imperativity is no big deal. Alas, it is not so for typical scripting.
It solves quite a few problem encountered in haskell. A typical example is the Int vs Integer problem:
mean l = sum l / length l -- doesn't compile because -- length returns an Int -- "Fractional Int" doesn't exist (which is normal)-- a solution is to define "length" with an open type ("sum" is not closed-world, -- otherwise one couldn't use it on [Float])
-- length2 :: Num a => [b] -> a length2 [] = 0 length2 (x:xs) = 1 + length2 xs
-- mean :: Fractional a => [a] -> a mean l = sum l / length2 l
merd has no problem using an Int in a division. Subsumption will help it transform in a larger type instancing Fractional.
"is_empty = (== [])" is ill-typed
compare merd's
not ~ member?
and Haskell's
curry (not . uncurry elem) or (not.) . elem or \x l -> not (elem x l)
This feature may seems like a gadget, but script programming use it very often
"filter (> 1) list" in Haskell is ok, so is "list.filter(> 1)" in merd (contrary to OCaml)
but you can still get weird thing in Haskell: "((>) 1)" is "\x -> 1 > x" whereas in merd ">" can't be partially applied this way, you'll get a syntax error. Another way to write it in merd is "(>)(,1)" which is "x -> (>)(x,1)" which is also "x -> x > 1"
The type of "fst(x,_) = x" is "x, _ -> x" instead of "a, b -> a" in Haskell and "'a * 'b -> 'a" in OCaml.
This may be get longer types, but these types are more readable.
Release: $Id: merd-vs-others.html.pl,v 1.16 2002/09/27 12:18:48 pixel_ Exp $