[[!toc]] The functional programming literature tends to use one of four languages: Scheme, OCaml, Standard ML (SML), or Haskell. With experience, you'll grow comfortable switching between these. At the beginning, though, it can be confusing. The easiest translations are between OCaml and SML. These languages are both derived from a common ancestor, ML. For the most part, the differences between them are only superficial. [Here's a translation manual](http://www.mpi-sws.org/~rossberg/sml-vs-ocaml.html). In some respects these languages are closer to Scheme than to Haskell: Scheme, OCaml and SML all default to call-by-value evaluation order, and all three have native syntax for mutation and other imperative idioms (though that's not central to their design). Haskell is different in both respects: the default evaluation order is call-by-name (strictly speaking, it's "call-by-need", which is a more efficient cousin), and the only way to have mutation or the like is through the use of monads. On both sides, however, the non-default evaluation order can also be had by using special syntax. And in other respects, OCaml and SML are more like Haskell than they are like Scheme. For example, OCaml and SML and Haskell all permit you to declare types and those types are *statically checked*: that is, your program won't even start to be interpreted unless all the types are consistent. In Scheme, on the other hand, type-checking only happens when your program is running, and the language is generally much laxer about what it accepts as well typed. (There's no problem having a list of mixed numbers and booleans, for example... and you don't need to wrap them in any sum type to do so.) Additionally, the syntax of OCaml and SML is superficially much closer to Haskell's than to Scheme's. #Comments, Whitespace, and Brackets# -- this is a single line comment in Haskell {- this is a multiline comment in Haskell -} (* this is a single or multiline comment in OCaml *) ; this is a single line comment in Scheme #| this is a multiline comment in Scheme |# #;(this is (another way to (comment out (a block) (of Scheme code)))) * Haskell is sensitive to linespace and indentation: it matters how your code is lined up. OCaml and Scheme don't care about this, though they recommend following some conventions for readability. * In Haskell, a block of code can be bracketed with `{` and `}`, with different expressions separated by `;`. But usually one would use line-breaks and proper indentation instead. In OCaml, separating expressions with `;` has a different meaning, having to do with how side-effects are sequenced. Instead, one can bracket a block of code with `(` and `)` or with `begin` and `end`. In Scheme, of course, every parentheses is significant. #Scheme and OCaml# * You can [try Scheme in your web browser](http://tryscheme.sourceforge.net/). This is useful if you don't have Racket or another Scheme implementation installed---but don't expect it to have all the bells and whistles of a mature implementation! * **Type Variants and Pattern Matching** If you want to reproduce this kind of OCaml code: # type lambda_expression = Var of char | Lam of char * lambda_expression | App of lambda_expression * lambda_expression;; # let rec free_vars (expr : lambda_expression) : char list = match expr with | Var label -> [label] | Lam (label, body) -> remove label (free_vars body) | App (left, right) -> merge (free_vars left) (free_vars right);; # free_vars (Lam ('x', (App (Var 'x', Var 'y'))));; - : char list = ['y'] in Scheme, you have two choices. First, the quick hack: ; we use the symbols 'var and 'lam as tags, and assume ; that an expression will always be a pair of one of these forms: ; (cons 'var symbol) ; (cons (cons 'lam symbol) expression) ; (cons expression expression) (define (free-vars expr) (cond [(eq? (car expr) 'var) (list (cdr expr))] [(and? (pair? (car expr)) (eq? (car (car expr)) 'lam)) (remove (cdr (car expr)) (free-vars (cdr expr)))] [else (merge (free-vars (car expr)) (free-vars (cdr expr)))])) Second, you can create real datatypes and pattern-match on them. There are several tools for doing this. I'll describe the `define-datatype` and `cases` forms developed for the book *Essentials of Programming Languages* (EoPL) by Friedman and Wand. (Alternatives include [the `struct` form in Racket](http://docs.racket-lang.org/guide/define-struct.html). Also `define-record-type` from srfi-9 and srfi-57; see also [the r6rs libs](http://docs.racket-lang.org/r6rs-lib-std/r6rs-lib-Z-H-7.html).) Here is how the tools from EoPL work. You must begin your file either with `#lang eopl` or with the first two lines below: #lang racket (require eopl/eopl) (define-datatype lambda-expression lambda-expression? (var (label symbol?)) (lam (label symbol?) (body lambda-expression?)) (app (left lambda-expression?) (right lambda-expression?))) (define (free-vars expr) (cases lambda-expression expr (var (label) (list label)) (lam (label body) (remove label (free-vars body))) (app (left right) (remove-duplicates (append (free-vars left) (free-vars right)))))) (free-vars (lam 'x (app (var 'x) (var 'y)))) ; evaluates to '(y) * Scheme has excellent support for working with implicit or "first-class" **continuations**, using either `call/cc` or any of various delimited continuation operators. See [the Racket docs](http://docs.racket-lang.org/reference/cont.html?q=shift&q=do#%28part._.Classical_.Control_.Operators%29). In Scheme you can use these forms by default (they're equivalent): (call/cc (lambda (k) ...)) (let/cc k ...) If your program declares `(require racket/control)`, you can also use: (begin ... (reset ... (shift k ...) ...) ...) (begin ... (prompt ... (control k ...) ...) ...) (begin ... (prompt ... (abort value) ...) ...) These last three forms are also available in OCaml, but to use them you'll need to compile and install Oleg Kiselyov's "delimcc" or "caml-shift" library (these names refer to the same library), which you can find [here](http://okmij.org/ftp/continuations/implementations.html#caml-shift). You'll already need to have OCaml installed. It also helps if you already have the findlib package installed, too, [as we discuss here](http://lambda.jimpryor.net/how_to_get_the_programming_languages_running_on_your_computer/). If you're not familiar with how to compile software on your computer, this might be beyond your reach for the time being. But assuming you do manage to compile and install Oleg's library, here's how you'd use it in an OCaml session: #require "delimcc";; (* loading Oleg's library this way requires the findlib package *) (* if you don't have findlib, you'll need to start ocaml like * this instead: ocaml -I /path/to/directory/containing/delimcc delimcc.cma *) open Delimcc;; (* this lets you say e.g. new_prompt instead of Delimcc.new_prompt *) let p = new_prompt ();; let prompt thunk = push_prompt p thunk;; let foo = ... prompt (fun () -> ... shift p (fun k -> ...) ... (* or *) control p (fun k -> ...) ... (* or *) abort p value ... ) ... There is also a library for using *undelimited* continuations in OCaml, but it's shakier than Oleg's delimited continuation library. There are some more hints about Scheme [here](/assignment8/) and [here](/week1/). We won't say any more here. #Haskell and OCaml# We will however try to give some general advice about how to translate between OCaml and Haskell. * Again, it may sometimes be useful to [try Haskell in your web browser](http://tryhaskell.org/) * There are many Haskell tutorials and textbooks available. This is probably the most actively developed: [Haskell wikibook](http://en.wikibooks.org/wiki/Haskell) * [Yet Another Haskell Tutorial](http://www.cs.utah.edu/~hal/docs/daume02yaht.pdf) (much of this excellent book has supposedly been integrated into the Haskell wikibook) * All About Monads has supposedly also been integrated into the Haskell wikibook * (A not-so-)[Gentle Introduction to Haskell](http://web.archive.org/web/http://www.haskell.org/tutorial/) (archived) * [Learn You a Haskell for Great Good](http://learnyouahaskell.com/) * [Another page comparing Haskell and OCaml](http://blog.ezyang.com/2010/10/ocaml-for-haskellers/) ##Type expressions## * In Haskell, you say a value has a certain type with: `value :: type`. You express the operation of prepending a new `int` to a list of `int`s with `1 : other_numbers`. In OCaml it's the reverse: you say `value : type` and `1 :: other_numbers`. * In Haskell, type names and constructors both begin with capital letters, and type variables always appear after their constructors, in Curried form. And the primary term for declaring a new type is `data` (short for [[!wikipedia algebraic data type]]). So we have: data Either a b = Left a | Right b; data FooType a b = Foo_constructor1 a b | Foo_constructor2 a b; In printed media, Haskell type variables are often written using Greek letters, like this:
type Either α β = Left α | Right β