Skip to main content

Meetup 23: Introduction to using Buck2 with OCaml

· 3 min read
Manas Jayanth
Managing Partner at Dining Philosophers, LLP.

Shubham Kumar lead the discussion on using OCaml with Buck2.

Additionally, Patate started a discussion about how monads unify exceptions with regular values. They also unify IO with regular values. My take, adding to his point, was how monads let us parameterise the execution model - they help us detach the meaning of computations from their representation and simply tell us what computation depends on what. The execution model, which could be swapped, then decides if it's an IO or error handling.

Here's a snippet that Patate graciously contributed

(* The program we are interested in is below.
* here are some preliminary definitions *)

(* IO monad *)
module IO: sig
type 'a t
val return: 'a -> 'a t
val bind: 'a t -> ('a -> 'b t) -> 'b t
val both: 'a t -> 'b t -> ('a * 'b) t
val wrap: (unit -> 'a) -> 'a t
val run: 'a t -> 'a
end = struct
type 'a t = unit -> 'a
let return x () = x
let bind io f () = f (io ()) ()
let both a b () = let a_result = a () in a_result, b ()
let wrap = Fun.id
let run io = io ()
end

(* Identity monad *)
module Id = struct
let return = Fun.id
let bind = ( |> )
let both a b = a, b
let run = Fun.id
end

(* ignores are for avoiding "unused" warnings *)
let () = ignore IO.(return, bind, both, wrap, run)
let () = ignore Id.(return, bind, both, run)

module Color = struct
type t = Red | Green | Blue

let to_string = function
| Red -> "red"
| Green -> "green"
| Blue -> "blue"

let of_string = function
| "red" -> Red
| "green" -> Green
| "blue" -> Blue
| s -> raise @@ Invalid_argument ("string_of_color \"" ^ s ^ "\"")
end

module IO_behaviour = struct
include IO
let get_name = IO.wrap (fun () -> input_line stdin)
let get_age = IO.wrap (fun () -> input_line stdin |> int_of_string)
let get_favorite_color = IO.wrap (fun () -> input_line stdin |> String.trim |> Color.of_string)
end
module Normal_behaviour = struct
include Id
let get_name = "patate"
let get_age = 42
let get_favorite_color = Color.Blue
end
let () = ignore (IO_behaviour.(get_name, get_age, get_favorite_color), Normal_behaviour.(get_name, get_age, get_favorite_color))
open Normal_behaviour
let () = ignore (get_name, get_age, get_favorite_color)

(* Program starts here *)

module Behaviour = Normal_behaviour
(* module Behaviour = Normal_behaviour *)

let ( let* ) = Behaviour.bind
let ( and* ) = Behaviour.both
let () = ignore (( let* ), ( and* ))

let result =
(*let open Behaviour in*)
let name = get_name
and age = get_age
and favorite_color_string = get_favorite_color in
(*Behaviour.return
@@*) Printf.sprintf
"%s is %dyo and likes %s"
name
age
(Color.to_string favorite_color_string)

let () = print_endline ((*Behaviour.run*) result)

When is the next meetup?

Checkout Upcoming Meetups

Stay in touch with us


Twitter: https://x.com/ReasonBangalore

Discord: https://discord.com/invite/Ytr36fRC4C

Meetup 22: Deploying OCaml apps

· One min read
Manas Jayanth
Managing Partner at Dining Philosophers, LLP.

I lead the discussion on deploying OCaml apps.

  1. CI/CD
  2. Prefix rewriting in esy and why it matters
  3. Static linking and building ocaml apps in Alpine
  4. Some Terraform scripts with Flatcar and docker provider to manage docker state remotely

and is the next meetup?

Checkout Upcoming Meetups

Stay in touch with us


Twitter: https://x.com/ReasonBangalore

Discord: https://discord.com/invite/Ytr36fRC4C

Article: Defunctionalisation

· 2 min read
Manas Jayanth
Managing Partner at Dining Philosophers, LLP.
Srijan Paul
CS and Math enthusiast

injuly proposed a talk on defunctionalisation in compilers - this rehashes his post defunctionalisation, but in OCaml. We recommend you read his post first and switch to this article to compare the OCaml implementation.

Higher order functions

let rec fold f acc = function
| [] -> acc
| x::xs -> f x (fold f acc xs)

let sum xs = fold (+) 0 xs
let add n xs = fold (fun x acc -> (x + n)::acc) [] xs

fold is the higher order function because it takes another function (possibly higher order), f.

The GADT representation of functions like function arguments

type ('env, 'params, 'return) arrow =
| FnPlus : int * int -> (unit, (int * int), int) arrow
| FnPlusCons : int * (int * int list) -> (int, (int * int list), int list) arrow

('env, 'params, 'return) arrow encodes functions that can be passed around as arguments to other functions.

fold (+) 0 xs could be expressed as fold (FnPlus (0, xs)) fold (fun x acc -> (x + n :: acc) could be expressed as fold (FnPlusCons (n, (x, acc)) where n is the free variable.

These representations can be evaluated with apply

let apply : type env params return.  (env, params, return) arrow -> return = fun arrow ->
match arrow with
| FnPlus (a, b) -> a + b
| FnPlusCons (n, (x, xs)) -> (n + x)::xs

Example output

utop[41]> apply (FnPlus (1, 2));;
- : int = 3
utop[42]> apply (FnPlusCons (10, (1, [2;3])));;
- : int args = [11; 2; 3]
utop[43]>

When is the next meetup?

Checkout Upcoming Meetups

Stay in touch with us


Twitter: https://x.com/ReasonBangalore

Discord: https://discord.com/invite/Ytr36fRC4C

Meetup 20: Parser Combinators in OCaml

· One min read
Manas Jayanth
Managing Partner at Dining Philosophers, LLP.

injuly live coded mini-angstrom drawing inspiration from both Angstrom and the Graham Hutton paper.

The agenda was

  1. Short intro to parser combinators and Monads.
  2. Re-implement angstrom from scratch – a popular parser combinator library.
  3. Go through angstrom's source, and compare differences from our implementation.
  4. Cover some popular Haskell parsing libraries (parsec, megaparsec, etc.)

After the meetup, @theteachr even raised a pull request to implement (WIP) a BenCode parser.

When is the next meetup?

Checkout Upcoming Meetups

Stay in touch with us


Twitter: https://x.com/ReasonBangalore

Discord: https://discord.com/invite/Ytr36fRC4C

Meetup 19: Bootstrapping OCaml projects without package manager

· One min read
Manas Jayanth
Managing Partner at Dining Philosophers, LLP.

We discussed what's needed by the package manager to make it's project bootstrap without itself.

Notably,

  1. Trace the complete transitive closure of the dependencies - so package manager must build such dependencies in isolation to catching missing dependencies.

  2. Trace the complete build environment variables

  3. A parser for .install files that ocaml build systems create to install built artifacts

A PoC with esy package manager can be found here

Checkout Upcoming Meetups

Stay in touch with us


Twitter: https://x.com/ReasonBangalore

Discord: https://discord.com/invite/Ytr36fRC4C

Meetup 7: Building a Monopoly Deal Clone

· One min read
Nick
YouTuber
Manas Jayanth
Managing Partner at Dining Philosophers, LLP.

We discussed encoding the rules of the popular card game Monopoly Deal in OCaml's type system with @theteachr.

Highlights

  • Briefly discussed the gameplay.
  • Incrementally built modules around relevant data structures.
  • Explored whether constructors could clash.
    • Initially, the general understanding was that it wouldn't work. Either the compilation would fail, or one of them would not be usable. But found out that it will just need some extra [type] hinting on some occasions to get the correct type.
  • Focussed on the principal of making invalid states impossible to represent.
  • Created an incomplete game state just to start getting some feedback.

You can find the code on Github


Once again, the discussion after lasted an hour. Srijan Paul shared how dithering in images can improve GIF color depths.

Next Meetup

We will be continue last week's Canvas tutorial, lead by AC Sreedhar in a meetup. More details here

Stay in touch with us


Twitter: https://twitter.com/ReasonBangalore

Discord: https://discord.com/invite/RamP7SCKcU

Meetup 16: Tree sitter for Reason and OCaml

· One min read
Manas Jayanth
Managing Partner at Dining Philosophers, LLP.

@Korven led the session explaining Tree sitter, how to write a grammar, debug tree-sitter-ocaml issies on helix. We even explored extending the Reason tree sitter grammar with a class definition. Here's the WIP snippet.

diff --git a/grammar.js b/grammar.js
index 9f587a4..7bc6748 100644
--- a/grammar.js
+++ b/grammar.js
@@ -56,6 +56,7 @@ module.exports = grammar(require("./embedded/ocaml"), {
$.expression_statement,
$.open_statement,
$.module_definition,
+ $.class_definition,
$.mutable_record_update,
),

@@ -71,6 +72,22 @@ module.exports = grammar(require("./embedded/ocaml"), {
$._semicolon,
),

+ class_definition: ($) =>
+ seq(
+ "class",
+ // TODO optional($._attribute),
+ $.class_binding,
+ $._semicolon,
+ ),
+
+ class_binding: ($) =>
+ seq(
+ field("name", choice($._class_name, alias("_", $.class_name))),
+ // optional($._module_typed),
+ seq("=", field("body", $._module_expression)),
+ // repeat($.item_attribute),
+ ),
+
module_binding: ($) =>
seq(
field("name", choice($._module_name, alias("_", $.module_name))),

Next Meetup

Checkout Upcoming Meetups

Stay in touch with us


Twitter: https://x.com/ReasonBangalore

Discord: https://discord.com/invite/Ytr36fRC4C

Meetup 15: Monopoly in OCaml continued - part 4

· One min read
Manas Jayanth
Managing Partner at Dining Philosophers, LLP.

@theteachr showed us the new game loop using Seq.of_dispenser making the game loop look like this.

let () =
let game =
[ "ocaml"; "reason"; "melange"; "dune" ]
|> List.map Player.make
|> Game.start
in
read_command
|> Seq.of_dispenser
|> Seq.filter_map Command.parse
|> Seq.scan Command.exec game
|> Seq.take_while Game.is_not_over
|> Seq.iter Tui.draw

Highlights

We discussed,

  1. Possible use of GADTs for handling propert card transformations. You can find the git branch here
  2. use of Seq.unfold. Code
  3. Modular card transformations. Code

Next Meetup

Checkout Upcoming Meetups

Stay in touch with us


Twitter: https://x.com/ReasonBangalore

Discord: https://discord.com/invite/Ytr36fRC4C