Skip to content

hydromatic/morel

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

52 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

smlj

Standard ML interpreter, implemented in Java

Requirements

Java version 8 or higher.

Get smlj

From Maven

Get smlj from Maven Central:

<dependency>
  <groupId>net.hydromatic</groupId>
  <artifactId>smlj</artifactId>
  <version>0.1.0</version>
</dependency>

Download and build

$ git clone git://github.com/julianhyde/smlj.git
$ cd smlj
$ ./mvnw install

On Windows, the last line is

> mvnw install

Run the shell

$ ./smlj
smlj version 0.1.0 (java version "11.0.4", JLine terminal, xterm-256color)
= "Hello, world!";
val it = "Hello, world!" : string
= exit
$

Status

Implemented:

  • Literals
  • Variables
  • Comments ((* block *) and (*) line)
  • let (expression that lets you define local variables and functions)
  • val (including val rec)
  • fun (declare function)
  • Operators: = <> < > <= >= ~ + - * / div mod ^ andalso orelse ::
  • Built-in constants and functions: it true false not
  • Type derivation
  • fn, function values, and function application
  • if
  • case
  • Primitive, list, tuple and record types
  • Type variables (polymorphism) (but see "bugs")
  • Enumerated types (datatype)
  • Tuples and unit, record and list values
  • Patterns (destructuring) in val and case, matching constants, wildcards, lists, records and tuples
  • Basis library functions: abs

Not implemented:

  • type
  • local
  • raise, handle
  • exception
  • while
  • References, and operators ! and :=
  • Operators: @ before
  • Constants: nil
  • User-defined operators (infix, infixr)
  • Type annotations in expressions and patterns

Bugs:

  • Unbound type variables are not yet supported. For example, the expression [] should have type 'a list but currently fails
  • Prevent user from overriding built-in constants and functions: true, false, nil, ref, it, ::; they should not be reserved
  • Access parameters and variables by offset into a fixed-size array; currently we address them by name, in a map that is copied far too often
  • Runtime should throw when divide by zero
  • Validator should give good user error when it cannot type an expression

Relational extensions

The from expression (and associated as, where and yield keywords) is a language extension to support relational algebra. It iterates over a list and generates another list.

In a sense, from is syntactic sugar. For example, given emps and depts, relations defined as lists of records as follows

val emps =
  [{id = 100, name = "Fred", deptno = 10},
   {id = 101, name = "Velma", deptno = 20},
   {id = 102, name = "Shaggy", deptno = 30};
   {id = 103, name = "Scooby", deptno = 30}];
val depts =
  [{deptno = 10, name = "Sales"},
   {deptno = 20, name = "Marketing"},
   {deptno = 30, name = "Engineering"},
   {deptno = 40, name = "Support"}];

the expression

from e in emps where (#deptno e = 30) yield (#id e)

is equivalent to standard ML

map (fn e => (#id e)) (filter (fn e => (#deptno e) = 30) emps)

with the where and yield clauses emulating the filter and map higher-order functions without the need for lambdas (fn).

Relational expressions are an experiment bringing the features of query languages such as SQL into a functional language. We believe that a little syntactic sugar, backed by a relational query planner, makes ML into a powerful and convenient tool for querying large data sets. Conversely, we want to see how SQL would look if it supported lambdas, function-values, polymorphism, pattern-matching, and removed the syntactic distinction between tables and collection-valued columns.

You can iterate over more than one collection, and therefore generate a join or a cartesian product:

from e in emps, d in depts
  where (#deptno e) = (#deptno d)
  yield {id = (#id e), deptno = (#deptno e), ename = (#name e), dname = (#name d)};

As in any ML expression, you can define functions within a from expression, and those functions can operate on lists. Thus we can implement equivalents of SQL's IN and EXISTS operators:

let
  fun in_ e [] = false
    | in_ e (h :: t) = e = h orelse (in_ e t)
in
  from e in emps
  where in_ (#deptno e) (from d in depts
                where (#name d) = "Engineering"
                yield (#deptno d))
  yield (#name e)
end

let
  fun exists [] = false
    | exists hd :: tl = true
in
  from e in emps
  where exists (from d in depts
                where (#deptno d) = (#deptno e)
                andalso (#name d) = "Engineering")
  yield (#name e)
end

In the second query, note that the sub-query inside the exists is correlated (references the e variable from the enclosing query) and skips the yield clause (because it doesn't matter which columns the sub-query returns, just whether it returns any rows).

More information

About

A functional query language

Resources

License

Stars

Watchers

Forks

Contributors 6

Languages