# HG changeset patch # User Adam Chlipala # Date 1310839934 14400 # Node ID 2f9b7382dd1ddfbbe71b200216a589fd279bb256 # Parent 841b089952592c8ccbc9e5995b741029173d1cda First tutorial chapter ready diff -r 841b08995259 -r 2f9b7382dd1d doc/intro.ur --- a/doc/intro.ur Sat Jul 16 11:48:11 2011 -0400 +++ b/doc/intro.ur Sat Jul 16 14:12:14 2011 -0400 @@ -6,9 +6,9 @@ (* This tutorial by Adam Chlipala is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License. *) -(* This is a tutorial for the Ur/Web programming language. The official project web site is your starting point for information, like a reference manual and where to download the latest code release. In this tutorial, we'll just focus on introducing the language features. *) +(* This is a tutorial for the Ur/Web programming language. The official project web site is your starting point for information, like a reference manual and a pointer to download the latest code release. In this tutorial, we'll just focus on introducing the language features. *) -(* Ur/Web contains a web-indendent core language called Ur, which will be the subject of the first few chapters of the tutorial. Ur inherits its foundation from ML and Haskell, then going further to add fancier stuff. This first chapter of the tutorial reviews the key ML and Haskell features, giving their syntax in Ur. *) +(* Ur/Web contains a web-indendent core language called Ur, which will be the subject of the first few chapters of the tutorial. Ur inherits its foundation from ML and Haskell, then going further to add fancier stuff. This first chapter of the tutorial reviews the key ML and Haskell features, giving their syntax in Ur. I do assume reading familiarity with ML and Haskell and won't dwell too much on explaining the imported features. *) (* * Basics *) @@ -52,6 +52,14 @@ fact 5 (* end *) +fun isEven n = n = 0 || isOdd (n - 1) +and isOdd n = n = 1 || isEven (n - 1) + +(* begin eval *) +isEven 32 +(* end *) + + (* Of course we have anonymous functions, too. *) val inc = fn x => x + 1 @@ -74,7 +82,7 @@ compose inc inc 3 (* end *) -(* The option type family is like ML's option or Haskell's Maybe. We also have a case expression form lifted directly from ML. Note that, while Ur follows most syntactic conventions of ML, one key difference is that type families appear before their arguments, as in Haskell. *) +(* The option type family is like ML's option or Haskell's Maybe. We also have a case expression form lifted directly from ML. Note that, while Ur follows most syntactic conventions of ML, one key difference is that type family names appear before their arguments, as in Haskell. *) fun predecessor (n : int) : option int = if n >= 1 then Some (n - 1) else None @@ -111,7 +119,7 @@ length strings (* end *) -(* And lists make a good setting for demonstrating higher-order functions and local functions. (This example also introduces one idiosyncrasy of Ur, which is that map is a keyword, so we name our"map" function mp. *) +(* And lists make a good setting for demonstrating higher-order functions and local functions. (This example also introduces one idiosyncrasy of Ur, which is that map is a keyword, so we name our "map" function mp.) *) (* begin hide *) fun show_list [t] (_ : show t) : show (list t) = @@ -219,7 +227,7 @@ (* * Borrowed from ML *) -(* Ur includes an ML-style module system. The most basic use case involves packaging abstract types with their "methods." *) +(* Ur includes an ML-style module system. The most basic use case involves packaging abstract types with their "methods." *) signature COUNTER = sig type t @@ -230,7 +238,7 @@ structure Counter : COUNTER = struct type t = int - val zero = 9 + val zero = 0 val increment = plus 1 fun toInt x = x end @@ -239,7 +247,7 @@ Counter.toInt (Counter.increment Counter.zero) (* end *) -(* We may package not just abstract types, but also abstract type families. Here we see our first use of the con keyword, which stands for constructor. Constructors are a generalization of types to include other "compile-time things"; for instance, type families, which are assigned the kind Type -> Type. Kinds are to constructors as types are to normal values. We also see how to write the type of a polymorphic function, using the ::: syntax for type variable binding. This ::: differs from the :: used with the con keyword because it marks a type parameter as implicit, so that it need not be supplied explicitly at call sites. Such an option is the only one available in ML and Haskell, but, in the next chapter, we'll meet cases where it is appropriate to use explicit constructor parameters. *) +(* We may package not just abstract types, but also abstract type families. Here we see our first use of the con keyword, which stands for constructor. Constructors are a generalization of types to include other "compile-time things"; for instance, basic type families, which are assigned the kind Type -> Type. Kinds are to constructors as types are to normal values. We also see how to write the type of a polymorphic function, using the ::: syntax for type variable binding. This ::: differs from the :: used with the con keyword because it marks a type parameter as implicit, so that it need not be supplied explicitly at call sites. Such an option is the only one available in ML and Haskell, but, in the next chapter, we'll meet cases where it is appropriate to use explicit constructor parameters. *) signature STACK = sig con t :: Type -> Type @@ -288,7 +296,7 @@ case t of Leaf => Node (Leaf, k, v, Leaf) | Node (left, k', v', right) => - case M.compare k' k of + case M.compare k k' of Equal => Node (left, k, v, right) | Less => Node (insert left k v, k', v', right) | Greater => Node (left, k', v', insert right k v) @@ -297,7 +305,7 @@ case t of Leaf => None | Node (left, k', v, right) => - case M.compare k' k of + case M.compare k k' of Equal => Some v | Less => lookup left k | Greater => lookup right k @@ -333,3 +341,93 @@ (* begin eval *) lookup (insert (insert empty 0 "A") 1 "B") 2 (* end *) + +(* Ur adopts OCaml's approach to splitting projects across source files. When a project contains files foo.ur and foo.urs, these are taken as defining a module named Foo whose signature is drawn from foo.urs and whose implementation is drawn from foo.ur. If foo.ur exists without foo.urs, then module Foo is defined without an explicit signature, so that it is assigned its principal signature, which exposes all typing details without abstraction. *) + + +(* * Borrowed from Haskell *) + +(* Ur includes a take on type classes. For instance, here is a generic "max" function that relies on a type class ord. Notice that the type class membership witness is treated like an ordinary function parameter, though we don't assign it a name here, because type inference figures out where it should be used. The more advanced examples of the next chapter will include cases where we manipulate type class witnesses explicitly. *) + +fun max [a] (_ : ord a) (x : a) (y : a) : a = + if x < y then + y + else + x + +(* begin eval *) +max 1 2 +(* end *) + +(* begin eval *) +max "ABC" "ABA" +(* end *) + +(* The idiomatic way to define a new type class is to stash it inside a module, like in this example: *) + +signature DOUBLE = sig + class double + val double : a ::: Type -> double a -> a -> a + val mkDouble : a ::: Type -> (a -> a) -> double a + + val double_int : double int + val double_string : double string +end + +structure Double : DOUBLE = struct + class double a = a -> a + + fun double [a] (f : double a) (x : a) : a = f x + fun mkDouble [a] (f : a -> a) : double a = f + + val double_int = mkDouble (times 2) + val double_string = mkDouble (fn s => s ^ s) +end + +open Double + +(* begin eval *) +double 13 +(* end *) + +(* begin eval *) +double "ho" +(* end *) + +val double_float = mkDouble (times 2.0) + +(* begin eval *) +double 2.3 +(* end *) + +(* That example had a mix of instances defined with a class and instances defined outside its module. Its possible to create closed type classes simply by omitting from the module an instance creation function like mkDouble. This way, only the instances you decide on may be allowed, which enables you to enforce program-wide invariants over instances. *) + +signature OK_TYPE = sig + class ok + val importantOperation : a ::: Type -> ok a -> a -> string + val ok_int : ok int + val ok_float : ok float +end + +structure OkType : OK_TYPE = struct + class ok a = unit + fun importantOperation [a] (_ : ok a) (_ : a) = "You found an OK value!" + val ok_int = () + val ok_float = () +end + +open OkType + +(* begin eval *) +importantOperation 13 +(* end *) + +(* Like Haskell, Ur supports the more general notion of constructor classes, whose instances may be parameterized over constructors with kinds beside Type. Also like in Haskell, the flagship constructor class is monad. Ur/Web's counterpart of Haskell's IO monad is transaction, which indicates the tight coupling with transactional execution in server-side code. Just as in Haskell, transaction must be used to create side-effecting actions, since Ur is purely functional (but has eager evaluation). Here is a quick example transaction, showcasing Ur's variation on Haskell do notation. *) + +val readBack : transaction int = + src <- source 0; + set src 1; + n <- get src; + return (n + 1) + +(* We get ahead of ourselves a bit here, as this example uses functions associated with client-side code to create and manipulate a mutable data source. *) diff -r 841b08995259 -r 2f9b7382dd1d src/c/static.c --- a/src/c/static.c Sat Jul 16 11:48:11 2011 -0400 +++ b/src/c/static.c Sat Jul 16 14:12:14 2011 -0400 @@ -37,6 +37,8 @@ fprintf(stderr, "Error: %s\n", uw_error_message(ctx)); return 1; } + + uw_reset(ctx); } } diff -r 841b08995259 -r 2f9b7382dd1d src/elisp/urweb-mode.el --- a/src/elisp/urweb-mode.el Sat Jul 16 11:48:11 2011 -0400 +++ b/src/elisp/urweb-mode.el Sat Jul 16 14:12:14 2011 -0400 @@ -170,8 +170,10 @@ (finished nil) (answer nil) ) - (while (and (not finished) (re-search-backward "[<>{}]" nil t)) + (while (and (not finished) (re-search-backward "[<>{}]|\\*\)" nil t)) (cond + ((looking-at "*)") + (search-backward "(*")) ((looking-at "{") (if (> depth 0) (decf depth) @@ -183,7 +185,7 @@ (looking-at "<>"))) (setq finished t)) ((or (looking-at "< ") (looking-at "<=")) - nil) + (setq finished t)) ((looking-at "<") (setq finished t)) ((looking-at ">")