merd =
Ruby-like expressivity +
static type checks (a la Haskell)
| |
09/01/2003
This project is currently on hold. Nothing is really usable yet, though quite a
few things are working.
I will keep on adding and modifying the documentation (especially concerning
similar projects).
- Resources
- Features
- Examples
- Why Yet Another Language
- Download
- FAQ
Resources
merd-devel is the mailing list about merd development.
A forum and various stuff is provided by
.
Like all good free software, merd is in CVS :)
You can access browse it using viewcvs
see sourceforge CVS help for more
Features
- function/method calling
- parametric and ad'hoc polymorphism (overloading/refining/specializing)
- late binding
- default parameters
- types
- strongly typed
- static typing (keeping expressivity!)
- type inference (see ML, and more precisely O'Haskell)
- subtyping (extendable structs, extendable datatype, subranges, bounded quantification...)
- abstract-types (mix-in's, aka type-classes)
- types as first-class citizen
- functional programming
- functions as first-class citizen (even overloaded one)
- anonymous functions
- partial application
- sugaring
- user definable operators (infix, prefix, suffix, multi-op (distfix), around-op)
- OO-like syntax (purely syntactic)
- indentation based grouping (see haskell&python), also called layout
- horizontal layout: 1+2 * 3 is (1+2) * 3 (merd innovation!)
- pattern-matching (see ML)
- macros (?)
- extensible powerful library
(work in progress,
inspired by many languages especially ruby)
- various choices explained
Examples
- A lot of examples taken from the Perl Cookbook
(flat file)
- Various examples:
##########
fact :=
0 -> 1
n -> n * fact(n-1)
fact'(n) := if n == 0 then 1 else n * fact'(n-1)
fact''(n) :=
fact = 1
while n > 0 do
fact *= n
n--
fact
##########
# overloading
print = print_int
print = print_string
[ 1, "a" ].map(print)
##########
# Lazy parameters
a && Lazy(b) = if a then b else False
a || Lazy(b) = if a then True else b
Lazy(a) or Lazy(b) := try a with _ -> b
before_leaving(Lazy(f), Lazy(f2)) =
try v = f2 ; f ; v
with e -> f ; raise e
local(expr, val, Lazy(f)) =
old = expr
expr = val
before_leaving(f, expr = old)
##########
# Partial application
map(,f) =
[] -> []
e:l -> f(e) : l.map(f)
double_list = map(, 2 *)
[1,2].double_list #=> [2,4]
Why Yet Another Language
Dynamic Languages
The language I really miss is alike Ruby: very usable with nice default
library. The success of Perl/Python/Ruby is partly due to their data
structures, especially lists/arrays, hashes and strings.
Alas, they miss static checks, Ruby being a dynamically typed languages, very
few checks are done at compile-time, making medium to big programs hard to
maintain.
As far as I know, few dynamically typed languages try to achieve compile-time
type checks on the overall program. The typical orientation is adding type
annotations (mainly for speedups and run-time checks). Dylan is an example of
mixing dynamic typing and explicit typing (or in Scheme,
bigloo)
Soft Typing
designates the effort of using type inference for dynamic languages.
Alas dynamic languages have a lot of features making it hard to infere types:
- heterogeneous data structures (it simplifies the language, the type declaration, no datatypes)
- incremental compilation/loading of packages
- ability to create function/methods at runtime
- use of the same variable with values of different types
- return values ignored (pb inherited from Algol68, useful when return value is not highly valued (error code...))
Static Languages
Compared to dynamic languages, statically typed languages are usually less
expressive or much more complicated:
- Explictly typed languages have the burden of giving the type of
everything.
Type inference based languages (Haskell, OCaml) regain some of the
expressivity (here is an introduction to type
inference see slide 15, or see this introduction to functional programming
in haskell).
But they usually loose some overloading capabilities (OCaml has
no overloading, Haskell has no ad'hoc overloading)
- Type declarations are needed
- Type reflexion needed to typecheck "printf"-like functions in a general
way (Cayenne)
- Genericity is harder to obtain (eg: templates in C++, C# 2, Java2, was missing in Java1)
Is this expressiveness loss too much? unavoidable?
Haskell
Haskell can hardly play in the dynamic languages ground due to its lazyness.
The transition between imperative to monads is a too big step.
OCaml
OCaml is a serious player with a lot of features:
- functional, OO and imperative programming
- good & fast interpreter + compiler + debugger
- polymorphic variants (enabling lightweight types and variant subtyping)
Alas, some bad points makes it badly suited for scripting
- no overloading (eg: "+." for floating point numbers, List.length vs Array.length)
- default library
- poor string functions, no good default regexp
- dictionnary (hash) not sugared enough
- too big separation between lists and arrays
- no overloading
- no string interpolation (like perl "foo is $foo" or ruby "foo is #{foo}")
- uncommon syntax (especially for imperative programming and OO)
- no data inheritance (only interface inheritance)
- elusive syntax error messages
- bad polymorphic variants error messages
Most bad points above do not need changing the language, or only a few
extensions. For example
G'Caml gets rid of
the lack of overloading. Camlp4
brings some solution to the syntax problems.
But I think OCaml is already too big a language to add new features.
Innovations
- try to fill the expressivity gap between the static typing world and the
dynamic typing world.
- clean core of values: datatypes, structs, abstract
types, functions, numbers...
- clean & complete overloading
(overloaded functions are first class)
- structural equivalence and open world types
- try hard to keep the more precise types
[ 1, "foo" ] !! [ 1 | "foo" ]
means the type of list [ 1, "foo" ] is a list of elements of type 1|"foo"
- existential-like types with subtyping:
[x,y] !! [Int|String] with x !! Int ; y !! String
means type of list [x,y] is a list of elements of type Int|String
[ 1, "foo" ].map(+ 1) is ill-typed, but
[ 1, "foo" ].map(+ 1|&|"bar") gives [ 2, "foobar" ]
- even abstract types are structural equivalence based.
- try avoiding the haskell Int vs Integer disease:
getStdRandom(randomR(1, 2)) fails with "Unresolved top-level overloading".
One solution is to allow a preferred type.
- use names only for error messages (and try very hard to
find the best names, since for error messages cost is not important)
- staged evaluation: execute at compile-time what's possible. This permits
to do some of the classical preprocessor work
(see pliant for a real life example, C++
templates also have the same power)
Slides
Here is a small presentation (generated with MagicPoint from this file)
Download
THIS IS PRE-ALPHA SOFTWARE. DO NOT EXPECT *ANYTHING*
Current version is 0.1.6.
- Source tarball
- binary rpm
- binary deb
Pixel
$Id: index.html.m4,v 1.15 2003/01/09 18:30:56 pixel_ Exp $
gipoco.com
is neither affiliated with the authors of this page or responsible
for its contents. This is a safe-cache copy of the original web site.
gipoco.com
is neither affiliated with the authors of this page nor responsible
for its contents. This is a safe-cache copy of the original web site.