Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make OCaml code samples closer to Haskell (chapters 1 to 4) #242

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions src/content/1.1/code/ocaml/snippet01.ml
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
module type Polymorphic_Function_F = sig
type a
type b

val f : a -> b
end
val f : a -> b
7 changes: 1 addition & 6 deletions src/content/1.1/code/ocaml/snippet02.ml
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
module type Polymorphic_Function_G = sig
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was definitely confusing for me when I started going through the ocaml book (as an ocaml beginner when I saw these modules). 👍

type b
type c

val g : b -> c
end
val g : b -> c
12 changes: 3 additions & 9 deletions src/content/1.1/code/ocaml/snippet03.ml
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
module Compose_Example
(F : Polymorphic_Function_F)
(G : Polymorphic_Function_G with type b = F.b) =
struct
(** OCaml doesn't have a compose operator. So, creating one. **)
let ( >> ) g f x = g (f x)
Copy link

@hyphenrf hyphenrf Oct 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was confusing because earlier F#'s chevron operator was mentioned which is the same >> operator but encodes forward composition. I have no idea why these examples were so excessively verbose! This isn't a good impression of the language's expressiveness

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok so I've read earlier issues that explain why it was this verbose. interesting POVs.


let compose : 'a -> 'c = G.g >> F.f
end
(** OCaml doesn't have a compose operator. So, creating one. **)
let ( % ) g f x = g (f x)
g % f
12 changes: 4 additions & 8 deletions src/content/1.1/code/ocaml/snippet04.ml
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
module Compose_Three_GF = functor(F:Polymorphic_Function_F)(G:Polymorphic_Function_G with type b = F.b)(H:Polymorphic_Function_H with type c = G.c) -> struct
let compose : 'a -> 'd = H.h >> (G.g >> F.f)
end
val f : a -> b
val g : b -> c
val h : c -> d

module Compose_Three_HG = functor(F:Polymorphic_Function_F)(G:Polymorphic_Function_G with type b = F.b)(H:Polymorphic_Function_H with type c = G.c) -> struct
let compose : 'a -> 'd = (H.h >> G.g) >> F.f
end

Compose_Three_GF.compose = Compose_Three_HG.compose
h % (g % f) = (h % g) % f = h % g % f
4 changes: 2 additions & 2 deletions src/content/1.1/code/ocaml/snippet06.ml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
f >> id
id >> f
f % id = f
id % f = f
4 changes: 1 addition & 3 deletions src/content/1.2/code/ocaml/snippet01.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
module type Chapter2_DeclareVariable = sig
val x : int
end
val x : int
1 change: 0 additions & 1 deletion src/content/1.2/code/ocaml/snippet010.ml

This file was deleted.

3 changes: 0 additions & 3 deletions src/content/1.2/code/ocaml/snippet011.ml

This file was deleted.

4 changes: 1 addition & 3 deletions src/content/1.2/code/ocaml/snippet02.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
module type Chapter2_DeclareFunction = sig
val f : bool -> bool
end
val f : bool -> bool
5 changes: 2 additions & 3 deletions src/content/1.2/code/ocaml/snippet03.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
module Chapter2_Bottom : Chapter2_DeclareFunction = struct
let f (b : bool) : bool = failwith "Not Implemented"
end
(* val f : bool -> bool *)
let f _x = failwith "Not Implemented"
5 changes: 2 additions & 3 deletions src/content/1.2/code/ocaml/snippet04.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
module Chapter2_Bottom : Chapter2_DeclareFunction = struct
let f : bool -> bool = fun _ -> failwith "Not implemented"
end
(* val f : bool -> bool *)
let f = failwith "Not Implemented"
15 changes: 14 additions & 1 deletion src/content/1.2/code/ocaml/snippet05.ml
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
let fact n = List.fold (List.range 1 n) ~init:1 ~f:( * )
(* OCaml doesn't ship with a lazy sequence range syntax nor a product
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I think I used Janestreet libs in a few places to reduce the snippet size and to move ahead quickly, but thanks for removing the dependency!

function; defining our own. *)

let rec range seq n1 n2 =
if n2 < n1 then seq
else n2 |> pred |> range (fun () -> Seq.Cons (n2, seq)) n1
let range = range Seq.empty

let rec product result seq = match seq () with
| Seq.Nil -> result
| Seq.Cons (n, seq) -> product (result * n) seq
let product = product 1

let fact n = product (range 1 n)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just

let factorial n = Seq.fold_left ( * ) 1 (Seq.init n succ)

or

let product = Seq.fold_left ( * ) 1
let factorial n = Seq.init n succ |> product

or use List instead of Seq (at your choice, in case you prefer to be more backwards-compatible) ?
It doesn't really matter that it's "lazy" does it? At least the book doesn't seem to make a point out of it.

3 changes: 1 addition & 2 deletions src/content/1.2/code/ocaml/snippet06.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
type void

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type void = |

or just use the word void directly, it's not like the haskell snippet has import Data.Void (Void, absurd)


let rec absurd (x : void) = absurd x
val absurd : void -> 'a
3 changes: 2 additions & 1 deletion src/content/1.2/code/ocaml/snippet07.ml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
let f44 () : int = 44
(* val f44 : unit -> int *)
let f44 () = 44
3 changes: 2 additions & 1 deletion src/content/1.2/code/ocaml/snippet08.ml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
let f_int (x : int) = ()
(* val f_int : int -> unit *)
let f_int x = ()
3 changes: 2 additions & 1 deletion src/content/1.2/code/ocaml/snippet09.ml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
let f_int (_ : int) = ()
(* val f_int :: int -> unit *)
let f_int _ = ()
1 change: 1 addition & 0 deletions src/content/1.2/code/ocaml/snippet10.ml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
(* val unit : 'a -> unit *)
let unit _ = ()
8 changes: 4 additions & 4 deletions src/content/1.3/code/ocaml/snippet01.ml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module type Monoid = sig
type a
module type MONOID = sig
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it an OCaml convention to capitalize the module type?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi yes I believe so, e.g. see http://www.mseri.me/typeclass-ocaml/

I will try to follow the ocaml conventions writing capitalized modules (e.g. Monoid) and fully capital signatures (e.g. MONOID).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the convention originates from SML, it's loosely followed in OCaml really (even stdlib doesn't follow it).

type t

val mempty : a
val mappend : a -> a -> a
val mempty : t
val mappend : t -> t -> t
end
12 changes: 7 additions & 5 deletions src/content/1.3/code/ocaml/snippet02.ml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module StringMonoid : Monoid = struct
type a = string
(* In OCaml, any module that defines a module type's members,
automatically conforms to that module type. It doesn't need to
explicitly declare that it conforms. *)

let mempty = ""
let mappend = ( ^ )
end
type t = string

let mempty = ""
let mappend = ( ^ )
8 changes: 1 addition & 7 deletions src/content/1.4/code/ocaml/snippet03.ml
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
module type Kleisli = sig
type a
type b
type c

val ( >=> ) : (a -> b writer) -> (b -> c writer) -> a -> c writer
end
val ( >=> ) : ('a -> 'b writer) -> ('b -> 'c writer) -> 'a -> 'c writer
5 changes: 4 additions & 1 deletion src/content/1.4/code/ocaml/snippet04.ml
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
let pure x = x, ""
let ( >=> ) m1 m2 = fun x ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The snippets are starting to diverge here but I hope they are in the right place in the book

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, the best way would probably be to run the LaTeX compilation and check the output, but from reading along the chapter I believe the sequence of the code samples now makes sense.

let y, s1 = m1 x in
let z, s2 = m2 y in
z, s1 ^ s2
5 changes: 2 additions & 3 deletions src/content/1.4/code/ocaml/snippet05.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
let up_case : string -> string writer =
fun s -> String.uppercase s, "up_case "
;;
(* val return : 'a -> 'a writer *)
let return x = x, ""
9 changes: 6 additions & 3 deletions src/content/1.4/code/ocaml/snippet06.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
let to_words : string -> string list writer =
fun s -> String.split s ~on:' ', "to_words "
;;
(* val up_case : string -> string writer
Note: OCaml strings are raw bytestrings, not UTF-8 encoded. *)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The book never mentions UTF-8 encoding.. Maybe this makes sense in an OCaml tutorial to note, but why here?

let up_case s = String.uppercase_ascii s, "up_case "

(* val to_words : string -> string list writer *)
let to_words s = String.split_on_char ' ' s, "to_words "
12 changes: 2 additions & 10 deletions src/content/1.4/code/ocaml/snippet07.ml
Original file line number Diff line number Diff line change
@@ -1,10 +1,2 @@
module KleisiExample
(K : Kleisli
with type a = string
and type b = string
and type c = string list) =
struct
let up_case_and_to_words : string -> string list writer =
K.( >=> ) up_case to_words
;;
end
(* val process : string -> string list writer *)
let process = up_case >=> to_words