···
3
+
(* Helper module for working with markdown book chapters *)
4
+
module BookChapter = struct
11
+
(* Book chapters as a series of markdown files *)
15
+
title = "# Introduction to OCaml";
17
+
# Introduction to OCaml
19
+
OCaml is a general-purpose, multi-paradigm programming language which extends the Caml dialect of ML with object-oriented features.
23
+
- **Strong Static Typing**: Catch errors at compile time rather than runtime
24
+
- **Type Inference**: No need to annotate every variable with a type
25
+
- **Pattern Matching**: Express complex control flow in a clear and concise way
26
+
- **Functional Programming**: First-class functions and immutability
27
+
- **Module System**: Powerful abstraction capabilities with modules and functors
28
+
- **Performance**: Native code compilation with excellent performance characteristics
32
+
OCaml was created in 1996 by Xavier Leroy, Jérôme Vouillon, Damien Doligez, and Didier Rémy at INRIA in France. It evolved from the Caml language, which itself was an implementation of ML.
36
+
OCaml offers a unique combination of features that make it particularly well-suited for certain domains:
38
+
- **Program Correctness**: The strong type system catches many errors at compile time
39
+
- **Symbolic Computing**: Excellent for manipulating complex data structures and symbolic expressions
40
+
- **Systems Programming**: Can be used for low-level systems programming with high safety guarantees
41
+
- **Web Development**: Modern frameworks like Dream make web development straightforward
43
+
In the following chapters, we'll explore the language features in depth and learn how to leverage OCaml's strengths for building robust, maintainable software.
48
+
title = "# Basic Syntax and Types";
50
+
# Basic Syntax and Types
52
+
OCaml has a clean, consistent syntax that emphasizes readability and minimizes boilerplate.
54
+
## Variables and Basic Types
56
+
In OCaml, variables are immutable by default. Once a value is bound to a name, that binding cannot change.
59
+
(* Binding a value to a name *)
61
+
let greeting = "Hello, World!"
63
+
(* OCaml has type inference *)
64
+
(* These are equivalent: *)
71
+
- `int`: Integer numbers
72
+
- `float`: Floating-point numbers
73
+
- `bool`: Boolean values (`true` or `false`)
74
+
- `char`: Single characters
75
+
- `string`: Text strings
76
+
- `unit`: The empty tuple, written `()`
80
+
Functions in OCaml are first-class values:
83
+
(* A simple function *)
86
+
(* With type annotations *)
87
+
let add (x : int) (y : int) : int = x + y
89
+
(* Anonymous (lambda) function *)
90
+
let increment = fun x -> x + 1
92
+
(* Partial application *)
94
+
let fifteen = add5 10 (* equals 15 *)
99
+
OCaml uses expressions rather than statements for control flow:
102
+
(* If expression *)
104
+
if x < 0 then -x else x
106
+
(* Match expression (pattern matching) *)
107
+
let describe_sign x =
109
+
| x when x < 0 -> "negative"
116
+
Functions need the `rec` keyword to be recursive:
119
+
(* Recursive function *)
120
+
let rec factorial n =
121
+
if n <= 1 then 1 else n * factorial (n - 1)
123
+
(* Mutually recursive functions *)
124
+
let rec is_even n =
125
+
if n = 0 then true else is_odd (n - 1)
127
+
if n = 0 then false else is_even (n - 1)
130
+
This introduction to basic syntax sets the foundation for understanding OCaml's more advanced features, which we'll explore in the next chapters.
135
+
title = "# Data Structures";
139
+
OCaml provides several built-in data structures and makes it easy to define custom ones.
143
+
Tuples are fixed-length collections of values that can have different types:
146
+
(* A pair of an int and a string *)
147
+
let person = (42, "Alice")
149
+
(* Extracting values with pattern matching *)
150
+
let (age, name) = person
152
+
(* Accessing elements *)
153
+
let age = fst person (* For pairs only *)
154
+
let name = snd person (* For pairs only *)
159
+
Records are named collections of values:
162
+
(* Defining a record type *)
166
+
email: string option;
169
+
(* Creating a record *)
173
+
email = Some "alice@example.com";
176
+
(* Accessing fields *)
177
+
let alices_name = alice.name
179
+
(* Functional update (creates a new record) *)
180
+
let alice_birthday = { alice with age = alice.age + 1 }
185
+
Variants (also called algebraic data types) represent values that can be one of several cases:
188
+
(* Defining a variant type *)
190
+
| Circle of float (* radius *)
191
+
| Rectangle of float * float (* width, height *)
192
+
| Triangle of float * float * float (* sides *)
194
+
(* Creating variants *)
195
+
let my_circle = Circle 2.5
196
+
let my_rectangle = Rectangle (4.0, 6.0)
198
+
(* Pattern matching with variants *)
201
+
| Circle r -> Float.pi *. r *. r
202
+
| Rectangle (w, h) -> w *. h
203
+
| Triangle (a, b, c) ->
204
+
let s = (a +. b +. c) /. 2.0 in
205
+
sqrt (s *. (s -. a) *. (s -. b) *. (s -. c))
210
+
Lists are immutable linked lists of elements of the same type:
213
+
(* Creating lists *)
215
+
let numbers = [1; 2; 3; 4; 5]
216
+
let constructed = 1 :: 2 :: 3 :: []
218
+
(* Pattern matching with lists *)
219
+
let rec sum_list lst =
222
+
| head :: tail -> head + sum_list tail
224
+
(* Common list functions *)
225
+
let doubled = List.map (fun x -> x * 2) numbers
226
+
let evens = List.filter (fun x -> x mod 2 = 0) numbers
227
+
let sum = List.fold_left (+) 0 numbers
232
+
Arrays provide mutable, fixed-size collections with O(1) random access:
235
+
(* Creating arrays *)
236
+
let arr = [|1; 2; 3; 4; 5|]
238
+
(* Accessing elements (0-indexed) *)
239
+
let first = arr.(0)
241
+
(* Modifying elements *)
242
+
let () = arr.(0) <- 10
244
+
(* Array functions *)
245
+
let doubled = Array.map (fun x -> x * 2) arr
250
+
The option type represents values that might be absent:
254
+
type 'a option = None | Some of 'a
256
+
(* Using options *)
257
+
let safe_divide x y =
258
+
if y = 0 then None else Some (x / y)
260
+
(* Working with options *)
261
+
match safe_divide 10 2 with
262
+
| None -> print_endline "Division by zero"
263
+
| Some result -> Printf.printf "Result: %d\n" result
266
+
These data structures form the backbone of OCaml programming and allow for expressing complex data relationships in a type-safe way.
271
+
title = "# Modules and Functors";
273
+
# Modules and Functors
275
+
OCaml's module system is one of its most powerful features. It allows for organizing code into reusable components with clear interfaces.
279
+
A module is a collection of related definitions (types, values, submodules, etc.):
282
+
(* Defining a module *)
283
+
module Math = struct
285
+
let square x = x *. x
286
+
let cube x = x *. x *. x
289
+
(* Using a module *)
290
+
let area_of_circle r = Math.pi *. Math.square r
293
+
## Module Signatures
295
+
Module signatures define the interface of a module, hiding implementation details:
298
+
(* Defining a signature *)
299
+
module type MATH = sig
301
+
val square : float -> float
302
+
val cube : float -> float
305
+
(* Implementing a signature *)
306
+
module Math : MATH = struct
308
+
let square x = x *. x
309
+
let cube x = x *. x *. x
311
+
(* This is hidden because it's not in the signature *)
312
+
let private_helper x = x +. 1.0
318
+
Functors are functions from modules to modules, allowing for higher-order modularity:
321
+
(* Module signature for collections *)
322
+
module type COLLECTION = sig
325
+
val add : 'a -> 'a t -> 'a t
326
+
val mem : 'a -> 'a t -> bool
329
+
(* Functor that creates a set implementation given an element type with comparison *)
330
+
module MakeSet (Element : sig type t val compare : t -> t -> int end) : COLLECTION with type 'a t = Element.t list = struct
331
+
type 'a t = Element.t list
335
+
let rec add x lst =
339
+
let c = Element.compare x y in
340
+
if c < 0 then x :: lst
341
+
else if c = 0 then lst (* Element already exists *)
344
+
let rec mem x lst =
348
+
let c = Element.compare x y in
350
+
else if c < 0 then false
354
+
(* Creating an integer set *)
355
+
module IntElement = struct
357
+
let compare = Int.compare
360
+
module IntSet = MakeSet(IntElement)
362
+
(* Using the set *)
363
+
let my_set = IntSet.empty
367
+
|> IntSet.add 1 (* Duplicate, not added *)
369
+
let has_three = IntSet.mem 3 my_set (* true *)
370
+
let has_five = IntSet.mem 5 my_set (* false *)
373
+
## First-Class Modules
375
+
OCaml also supports first-class modules, allowing modules to be passed as values:
378
+
(* Module type for number operations *)
379
+
module type NUMBER = sig
382
+
val add : t -> t -> t
383
+
val to_string : t -> string
386
+
(* Implementations for different number types *)
387
+
module Int : NUMBER with type t = int = struct
391
+
let to_string = string_of_int
394
+
module Float : NUMBER with type t = float = struct
398
+
let to_string = string_of_float
401
+
(* Function that works with any NUMBER module *)
402
+
let sum_as_string (type a) (module N : NUMBER with type t = a) numbers =
403
+
let sum = List.fold_left N.add N.zero numbers in
406
+
(* Using first-class modules *)
407
+
let int_sum = sum_as_string (module Int) [1; 2; 3; 4]
408
+
let float_sum = sum_as_string (module Float) [1.0; 2.5; 3.7]
411
+
## Open and Include
413
+
OCaml provides ways to bring module contents into scope:
416
+
(* Open brings module contents into scope temporarily *)
421
+
(* Local opening with the modern syntax *)
422
+
let area = Math.(pi *. square 2.0)
424
+
(* Include actually extends a module with another module's contents *)
425
+
module ExtendedMath = struct
427
+
let tau = 2.0 *. pi
428
+
let circumference r = tau *. r
432
+
The module system enables OCaml programmers to build highly modular, reusable code with clear boundaries between components.
437
+
title = "# Advanced Features";
439
+
# Advanced Features
441
+
OCaml offers several advanced features that set it apart from other languages. This chapter explores some of the more powerful language constructs.
443
+
## Polymorphic Variants
445
+
Unlike regular variants, polymorphic variants don't need to be predefined:
448
+
(* Using polymorphic variants directly *)
449
+
let weekend = `Saturday | `Sunday
450
+
let is_weekend day =
452
+
| `Saturday | `Sunday -> true
453
+
| `Monday .. `Friday -> false
455
+
(* Can be used in mixed contexts *)
456
+
let shape_area = function
457
+
| `Circle r -> Float.pi *. r *. r
458
+
| `Rectangle (w, h) -> w *. h
459
+
| `Triangle (b, h) -> 0.5 *. b *. h
460
+
| `Regular_polygon(n, s) when n >= 3 ->
461
+
let apothem = s /. (2.0 *. tan (Float.pi /. float_of_int n)) in
462
+
n *. s *. apothem /. 2.0
463
+
| _ -> failwith "Invalid shape"
466
+
## Objects and Classes
468
+
OCaml supports object-oriented programming:
471
+
(* Simple class definition *)
472
+
class point x_init y_init =
474
+
val mutable x = x_init
475
+
val mutable y = y_init
479
+
method move dx dy = x <- x + dx; y <- y + dy
480
+
method distance_from_origin =
481
+
sqrt (float_of_int (x * x + y * y))
483
+
(* Private method *)
484
+
method private to_string =
485
+
Printf.sprintf "(%d, %d)" x y
487
+
(* Calling another method *)
488
+
method print = print_endline self#to_string
491
+
(* Using a class *)
492
+
let p = new point 3 4
493
+
let () = p#move 2 1
494
+
let d = p#distance_from_origin
497
+
## Generalized Algebraic Data Types (GADTs)
499
+
GADTs provide more type control than regular variants:
502
+
(* A GADT for type-safe expressions *)
504
+
| Int : int -> int expr
505
+
| Bool : bool -> bool expr
506
+
| Add : int expr * int expr -> int expr
507
+
| Eq : 'a expr * 'a expr -> bool expr
509
+
(* Type-safe evaluation *)
510
+
let rec eval : type a. a expr -> a = function
513
+
| Add (e1, e2) -> eval e1 + eval e2
514
+
| Eq (e1, e2) -> eval e1 = eval e2
516
+
(* These expressions are statically type-checked *)
517
+
let e1 = Add (Int 1, Int 2) (* OK: int expr *)
518
+
let e2 = Eq (Int 1, Int 2) (* OK: bool expr *)
519
+
(* let e3 = Add (Int 1, Bool true) (* Type error! *) *)
520
+
(* let e4 = Eq (Int 1, Bool true) (* Type error! *) *)
525
+
OCaml allows extending existing types:
528
+
(* Original type *)
529
+
type shape = Circle of float | Rectangle of float * float
531
+
(* Extending the type in another module *)
532
+
type shape += Triangle of float * float * float
534
+
(* Pattern matching must now handle unknown cases *)
535
+
let area = function
536
+
| Circle r -> Float.pi *. r *. r
537
+
| Rectangle (w, h) -> w *. h
538
+
| Triangle (a, b, c) ->
539
+
let s = (a +. b +. c) /. 2.0 in
540
+
sqrt (s *. (s -. a) *. (s -. b) *. (s -. c))
541
+
| _ -> failwith "Unknown shape"
544
+
## Effects and Effect Handlers
546
+
OCaml 5 introduced algebraic effects for managing control flow:
549
+
(* Defining an effect *)
550
+
type _ Effect.t += Ask : string -> string Effect.t
552
+
(* Handler for the Ask effect *)
553
+
let prompt_user () =
554
+
Effect.Deep.try_with
556
+
let name = Effect.perform (Ask "What is your name?") in
557
+
Printf.printf "Hello, %s!\n" name)
558
+
{ Effect.Deep.effc = fun (type a) (effect : a Effect.t) ->
560
+
| Ask prompt -> fun k ->
561
+
Printf.printf "%s " prompt;
562
+
let response = read_line () in
567
+
## Higher-Ranked Polymorphism
569
+
Using the `Obj.magic` escape hatch (with caution):
572
+
(* This would normally not be permitted due to rank-2 polymorphism *)
573
+
let apply_to_all_types f =
574
+
let magic_f : 'a -> string = Obj.magic f in
582
+
(* Usage - with great care! *)
583
+
let result = apply_to_all_types (fun x -> Printf.sprintf "Value: %s" (Obj.magic x))
586
+
## Metaprogramming with PPX
588
+
OCaml's PPX system enables powerful metaprogramming:
591
+
(* With ppx_deriving *)
595
+
email: string option;
596
+
} [@@deriving show, eq, ord]
598
+
(* With ppx_sexp_conv *)
603
+
} [@@deriving sexp]
605
+
(* With ppx_let for monadic operations *)
608
+
let* x = get_value_from_db "key1" in
609
+
let* y = get_value_from_db "key2" in
614
+
## Modules for Advanced Typing
616
+
Using modules to encode complex type relationships:
619
+
(* Phantom types for added type safety *)
620
+
module SafeString : sig
623
+
(* Constructors for different string types *)
624
+
val of_raw : string -> [`Raw] t
625
+
val sanitize : [`Raw] t -> [`Sanitized] t
626
+
val validate : [`Sanitized] t -> [`Validated] t option
628
+
(* Operations that require specific string types *)
629
+
val to_html : [`Sanitized] t -> string
630
+
val to_sql : [`Validated] t -> string
632
+
(* Common operations for all string types *)
633
+
val length : _ t -> int
634
+
val concat : _ t -> _ t -> [`Raw] t
639
+
let sanitize s = String.map (function '<' | '>' -> '_' | c -> c) s
640
+
let validate s = if String.length s > 0 then Some s else None
643
+
let to_sql s = "'" ^ String.map (function '\'' -> '\'' | c -> c) s ^ "'"
645
+
let length = String.length
646
+
let concat s1 s2 = s1 ^ s2
650
+
These advanced features make OCaml a uniquely powerful language for expressing complex programs with strong guarantees about correctness.
655
+
(* Get a chapter by ID *)
657
+
try Some (List.find (fun c -> c.id = id) chapters)
658
+
with Not_found -> None
660
+
(* Get chapter titles *)
661
+
let get_all_titles () =
662
+
List.map (fun c -> (c.id, c.title)) chapters
665
+
(* Create a server *)
666
+
let server = create_server
667
+
~name:"OCaml MCP Book Resource Example"
668
+
~version:"0.1.0" () |>
670
+
(* Set default capabilities *)
671
+
configure_server server
673
+
~with_resources:true
674
+
~with_resource_templates:true
675
+
~with_prompts:false ()
677
+
(* Add a resource template to get book chapters *)
678
+
let _ = add_resource_template server
679
+
~uri_template:"book/chapter/{id}"
680
+
~name:"Chapter Resource"
681
+
~description:"Get a specific chapter from the OCaml book by its ID"
682
+
~mime_type:"text/markdown"
686
+
(match BookChapter.get_by_id id with
687
+
| Some chapter -> chapter.contents
688
+
| None -> Printf.sprintf "# Error\n\nChapter with ID '%s' not found." id)
689
+
| _ -> "# Error\n\nInvalid parameters. Expected chapter ID."
692
+
(* Add a regular resource to get table of contents (no variables) *)
693
+
let _ = add_resource server
695
+
~name:"Table of Contents"
696
+
~description:"Get the table of contents for the OCaml book"
697
+
~mime_type:"text/markdown"
699
+
let titles = BookChapter.get_all_titles() in
700
+
let toc = "# OCaml Book - Table of Contents\n\n" ^
701
+
(List.mapi (fun i (id, title) ->
702
+
Printf.sprintf "%d. [%s](book/chapter/%s)\n"
704
+
(String.sub title 2 (String.length title - 2)) (* Remove "# " prefix *)
706
+
) titles |> String.concat "")
711
+
(* Add a regular resource for a complete book (no variables) *)
712
+
let _ = add_resource server
713
+
~uri:"book/complete"
714
+
~name:"Full contents"
715
+
~description:"Get the complete OCaml book as a single document"
716
+
~mime_type:"text/markdown"
718
+
let chapter_contents = List.map (fun c -> c.BookChapter.contents) BookChapter.chapters in
719
+
let content = "# The OCaml Book\n\n*A comprehensive guide to OCaml programming*\n\n" ^
720
+
(String.concat "\n\n---\n\n" chapter_contents)
725
+
(* Run the server with the default scheduler *)
727
+
Eio_main.run @@ fun env ->
728
+
Mcp_server.run_server env server