comparison doc/intro.ur @ 1502:2f9b7382dd1d

First tutorial chapter ready
author Adam Chlipala <adam@chlipala.net>
date Sat, 16 Jul 2011 14:12:14 -0400
parents 841b08995259
children d236dbf1b3e3
comparison
equal deleted inserted replaced
1501:841b08995259 1502:2f9b7382dd1d
4 val show_string = mkShow (fn s => "\"" ^ s ^ "\"") 4 val show_string = mkShow (fn s => "\"" ^ s ^ "\"")
5 (* end *) 5 (* end *)
6 6
7 (* This tutorial by <a href="http://adam.chlipala.net/">Adam Chlipala</a> is licensed under a <a href="http://creativecommons.org/licenses/by-nc-nd/3.0/">Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License</a>. *) 7 (* This tutorial by <a href="http://adam.chlipala.net/">Adam Chlipala</a> is licensed under a <a href="http://creativecommons.org/licenses/by-nc-nd/3.0/">Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License</a>. *)
8 8
9 (* This is a tutorial for the <a href="http://www.impredicative.com/ur/">Ur/Web</a> programming language. The <a href="http://www.impredicative.com/ur/">official project web site</a> 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. *) 9 (* This is a tutorial for the <a href="http://www.impredicative.com/ur/">Ur/Web</a> programming language. The <a href="http://www.impredicative.com/ur/">official project web site</a> 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. *)
10 10
11 (* 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. *) 11 (* 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. *)
12 12
13 (* * Basics *) 13 (* * Basics *)
14 14
15 (* Let's start with features shared with both ML and Haskell. First, we have the basic numeric, string, and Boolean stuff. (In the following examples, <tt>==</tt> is used to indicate the result of evaluating an expression. It's not valid Ur syntax!) *) 15 (* Let's start with features shared with both ML and Haskell. First, we have the basic numeric, string, and Boolean stuff. (In the following examples, <tt>==</tt> is used to indicate the result of evaluating an expression. It's not valid Ur syntax!) *)
16 16
50 50
51 (* begin eval *) 51 (* begin eval *)
52 fact 5 52 fact 5
53 (* end *) 53 (* end *)
54 54
55 fun isEven n = n = 0 || isOdd (n - 1)
56 and isOdd n = n = 1 || isEven (n - 1)
57
58 (* begin eval *)
59 isEven 32
60 (* end *)
61
62
55 (* Of course we have anonymous functions, too. *) 63 (* Of course we have anonymous functions, too. *)
56 64
57 val inc = fn x => x + 1 65 val inc = fn x => x + 1
58 66
59 (* begin eval *) 67 (* begin eval *)
72 80
73 (* begin eval *) 81 (* begin eval *)
74 compose inc inc 3 82 compose inc inc 3
75 (* end *) 83 (* end *)
76 84
77 (* The <tt>option</tt> type family is like ML's <tt>option</tt> or Haskell's <tt>Maybe</tt>. We also have a <tt>case</tt> 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. *) 85 (* The <tt>option</tt> type family is like ML's <tt>option</tt> or Haskell's <tt>Maybe</tt>. We also have a <tt>case</tt> 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. *)
78 86
79 fun predecessor (n : int) : option int = if n >= 1 then Some (n - 1) else None 87 fun predecessor (n : int) : option int = if n >= 1 then Some (n - 1) else None
80 88
81 (* begin hide *) 89 (* begin hide *)
82 fun show_option [t] (_ : show t) : show (option t) = 90 fun show_option [t] (_ : show t) : show (option t) =
109 117
110 (* begin eval *) 118 (* begin eval *)
111 length strings 119 length strings
112 (* end *) 120 (* end *)
113 121
114 (* 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 <tt>map</tt> is a keyword, so we name our"map" function <tt>mp</tt>. *) 122 (* 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 <tt>map</tt> is a keyword, so we name our "map" function <tt>mp</tt>.) *)
115 123
116 (* begin hide *) 124 (* begin hide *)
117 fun show_list [t] (_ : show t) : show (list t) = 125 fun show_list [t] (_ : show t) : show (list t) =
118 mkShow (let 126 mkShow (let
119 fun shower (ls : list t) = 127 fun shower (ls : list t) =
217 (* end *) 225 (* end *)
218 226
219 227
220 (* * Borrowed from ML *) 228 (* * Borrowed from ML *)
221 229
222 (* Ur includes an ML-style module system. The most basic use case involves packaging abstract types with their "methods." *) 230 (* Ur includes an ML-style <b>module system</b>. The most basic use case involves packaging abstract types with their "methods." *)
223 231
224 signature COUNTER = sig 232 signature COUNTER = sig
225 type t 233 type t
226 val zero : t 234 val zero : t
227 val increment : t -> t 235 val increment : t -> t
228 val toInt : t -> int 236 val toInt : t -> int
229 end 237 end
230 238
231 structure Counter : COUNTER = struct 239 structure Counter : COUNTER = struct
232 type t = int 240 type t = int
233 val zero = 9 241 val zero = 0
234 val increment = plus 1 242 val increment = plus 1
235 fun toInt x = x 243 fun toInt x = x
236 end 244 end
237 245
238 (* begin eval *) 246 (* begin eval *)
239 Counter.toInt (Counter.increment Counter.zero) 247 Counter.toInt (Counter.increment Counter.zero)
240 (* end *) 248 (* end *)
241 249
242 (* We may package not just abstract types, but also abstract type families. Here we see our first use of the <tt>con</tt> keyword, which stands for <b>constructor</b>. Constructors are a generalization of types to include other "compile-time things"; for instance, type families, which are assigned the kind <tt>Type -> Type</tt>. Kinds are to constructors as types are to normal values. We also see how to write the type of a polymorphic function, using the <tt>:::</tt> syntax for type variable binding. This <tt>:::</tt> differs from the <tt>::</tt> used with the <tt>con</tt> 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. *) 250 (* We may package not just abstract types, but also abstract type families. Here we see our first use of the <tt>con</tt> keyword, which stands for <b>constructor</b>. Constructors are a generalization of types to include other "compile-time things"; for instance, basic type families, which are assigned the <b>kind</b> <tt>Type -> Type</tt>. Kinds are to constructors as types are to normal values. We also see how to write the type of a polymorphic function, using the <tt>:::</tt> syntax for type variable binding. This <tt>:::</tt> differs from the <tt>::</tt> used with the <tt>con</tt> 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. *)
243 251
244 signature STACK = sig 252 signature STACK = sig
245 con t :: Type -> Type 253 con t :: Type -> Type
246 val empty : a ::: Type -> t a 254 val empty : a ::: Type -> t a
247 val push : a ::: Type -> t a -> a -> t a 255 val push : a ::: Type -> t a -> a -> t a
286 294
287 fun insert [a] (t : t a) (k : key) (v : a) : t a = 295 fun insert [a] (t : t a) (k : key) (v : a) : t a =
288 case t of 296 case t of
289 Leaf => Node (Leaf, k, v, Leaf) 297 Leaf => Node (Leaf, k, v, Leaf)
290 | Node (left, k', v', right) => 298 | Node (left, k', v', right) =>
291 case M.compare k' k of 299 case M.compare k k' of
292 Equal => Node (left, k, v, right) 300 Equal => Node (left, k, v, right)
293 | Less => Node (insert left k v, k', v', right) 301 | Less => Node (insert left k v, k', v', right)
294 | Greater => Node (left, k', v', insert right k v) 302 | Greater => Node (left, k', v', insert right k v)
295 303
296 fun lookup [a] (t : t a) (k : key) : option a = 304 fun lookup [a] (t : t a) (k : key) : option a =
297 case t of 305 case t of
298 Leaf => None 306 Leaf => None
299 | Node (left, k', v, right) => 307 | Node (left, k', v, right) =>
300 case M.compare k' k of 308 case M.compare k k' of
301 Equal => Some v 309 Equal => Some v
302 | Less => lookup left k 310 | Less => lookup left k
303 | Greater => lookup right k 311 | Greater => lookup right k
304 end 312 end
305 313
331 open IT 339 open IT
332 340
333 (* begin eval *) 341 (* begin eval *)
334 lookup (insert (insert empty 0 "A") 1 "B") 2 342 lookup (insert (insert empty 0 "A") 1 "B") 2
335 (* end *) 343 (* end *)
344
345 (* Ur adopts OCaml's approach to splitting projects across source files. When a project contains files <tt>foo.ur</tt> and <tt>foo.urs</tt>, these are taken as defining a module named <tt>Foo</tt> whose signature is drawn from <tt>foo.urs</tt> and whose implementation is drawn from <tt>foo.ur</tt>. If <tt>foo.ur</tt> exists without <tt>foo.urs</tt>, then module <tt>Foo</tt> is defined without an explicit signature, so that it is assigned its <b>principal signature</b>, which exposes all typing details without abstraction. *)
346
347
348 (* * Borrowed from Haskell *)
349
350 (* Ur includes a take on <b>type classes</b>. For instance, here is a generic "max" function that relies on a type class <tt>ord</tt>. 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. *)
351
352 fun max [a] (_ : ord a) (x : a) (y : a) : a =
353 if x < y then
354 y
355 else
356 x
357
358 (* begin eval *)
359 max 1 2
360 (* end *)
361
362 (* begin eval *)
363 max "ABC" "ABA"
364 (* end *)
365
366 (* The idiomatic way to define a new type class is to stash it inside a module, like in this example: *)
367
368 signature DOUBLE = sig
369 class double
370 val double : a ::: Type -> double a -> a -> a
371 val mkDouble : a ::: Type -> (a -> a) -> double a
372
373 val double_int : double int
374 val double_string : double string
375 end
376
377 structure Double : DOUBLE = struct
378 class double a = a -> a
379
380 fun double [a] (f : double a) (x : a) : a = f x
381 fun mkDouble [a] (f : a -> a) : double a = f
382
383 val double_int = mkDouble (times 2)
384 val double_string = mkDouble (fn s => s ^ s)
385 end
386
387 open Double
388
389 (* begin eval *)
390 double 13
391 (* end *)
392
393 (* begin eval *)
394 double "ho"
395 (* end *)
396
397 val double_float = mkDouble (times 2.0)
398
399 (* begin eval *)
400 double 2.3
401 (* end *)
402
403 (* That example had a mix of instances defined with a class and instances defined outside its module. Its possible to create <b>closed type classes</b> simply by omitting from the module an instance creation function like <tt>mkDouble</tt>. This way, only the instances you decide on may be allowed, which enables you to enforce program-wide invariants over instances. *)
404
405 signature OK_TYPE = sig
406 class ok
407 val importantOperation : a ::: Type -> ok a -> a -> string
408 val ok_int : ok int
409 val ok_float : ok float
410 end
411
412 structure OkType : OK_TYPE = struct
413 class ok a = unit
414 fun importantOperation [a] (_ : ok a) (_ : a) = "You found an OK value!"
415 val ok_int = ()
416 val ok_float = ()
417 end
418
419 open OkType
420
421 (* begin eval *)
422 importantOperation 13
423 (* end *)
424
425 (* Like Haskell, Ur supports the more general notion of <b>constructor classes</b>, whose instances may be parameterized over constructors with kinds beside <tt>Type</tt>. Also like in Haskell, the flagship constructor class is <tt>monad</tt>. Ur/Web's counterpart of Haskell's <tt>IO</tt> monad is <tt>transaction</tt>, which indicates the tight coupling with transactional execution in server-side code. Just as in Haskell, <tt>transaction</tt> 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 <tt>do</tt> notation. *)
426
427 val readBack : transaction int =
428 src <- source 0;
429 set src 1;
430 n <- get src;
431 return (n + 1)
432
433 (* 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. *)