How to use ppx_show

Hey, I am new to Ocaml, and trying to understand how the language works.
My current struggle is understand how to add ppx_show on types and classes.

Coming form Java or Rust its very simple to print objects. Either have a .toString generated on the objects, or add a Debug Trait for rust.

Now in Ocaml I found ppx_show, but material on how to use it is much to be desired.

Here is my case and my dune config.

(

executable
 (public_name order_manager)
 (name main)
 (preprocess(pps ppx_deriving.show ppx_deriving.ord))
 (libraries orderbook lwt sexplib ppx_show.runtime)
 (modules main))

As for the code:


module OrderEntry = struct
  include Showable

  type t = {securityId: string ; price: int ; qty: int ; } [@@deriving show]
end 

module PriceBook(S: SideType) = 
  struct

    type t = { id: string ; price: int ; side: string ; mutable price_map: OrderEntry.t list } [@@deriving show] ;;

    let init_book p = { id = "0001" ; price = p ; side = "BID" ; price_map = [] }
end;;

module SellPriceBook = PriceBook(struct let side = Sell end)

let pb = SellPriceBook.init_book

let () = show pb 

File "bin/main.ml", line 8, characters 9-13:
8 | let () = show pb 
             ^^^^
Error: Unbound value show

What am missing to be able to print the content of the types ?

1 Like

Hello! Can you try:

let pb = SellPriceBook.init_book 42 (* was missing a price *)

let str = SellPriceBook.show pb (* was missing the module *)

let () = print_endline str (* show returns a string, not unit *)

To clarify, the PPX automatically defines the show function for your type next to it, in the module SellPriceBook. The show function is not defined globally which is why you got the unbound error :slight_smile:

You should also be able to drop the ppx_show.runtime from your dune file (as you are actually using ppx_deriving.show and not ppx_show)

Thanks for the comment and pointing out my mistakes.

I have tried the suggestion, and it is still complaining.

File "bin/main.ml", line 8, characters 10-28:
8 | let str = SellPriceBook.show pb (* was missing the module *)
              ^^^^^^^^^^^^^^^^^^
Error: Unbound value SellPriceBook.show

It would make sense that the function is generated under the module where the derived type is. But for some reason its not binding to the module ?

If you use VsCode + right extension, you can mouse hover over module name, to see if stringification functions appeared.

In general, it would be much easier, if you upload a demo.

1 Like

Do you have a signature on the functor PriceBook in the real code? (or an .mli file?) The show function must be exported if you want to use it from the outside :slight_smile: (you can also add the [@@deriving show] in the signature/mli near the type t)

(Ah, and in case your code is split between a library and an executable, you also need the (preprocess (pps ppx_deriving.show etc)) in the library dune file, not only on the final executable)

1 Like

Thats right it in its own lib folder.

Here is some details, probably should of been included from the start :slight_smile:


(library
  (preprocess (pps ppx_deriving.show ppx_deriving.ord))
  (flags (:standard -w -66))
  (libraries fmlib_std core base sexplib)
  (name orderbook)
  (modules orderbook))

(executable
 (public_name order_service)
 (name main)
 (preprocess (pps ppx_deriving.show ppx_deriving.ord))
 (libraries orderbook lwt sexplib)
  (modules main))

Main.ml

open! Base
open Orderbook

module SellPriceBook = PriceBook(struct let side = Sell end)

let pb = SellPriceBook.init_book 42 (* was missing a price *)

let str = SellPriceBook.show pb (* was missing the module *)

let () = print_endline str (* show returns a string, not unit *)

orderbook.ml


open Base
open Core

type side = Buy | Sell [@@deriving show]
module type SideType = sig
    val side : side 
end

module OrderEntry = struct
  type t = {securityId: string ; price: int ; qty: int ; } [@@deriving show]
end 

module PriceBook(S: SideType) = 
  struct



    type price_book_level = { id: string ; price: int ; side: string ; mutable price_map: OrderEntry.t list } [@@deriving show] ;;

    let init_book p = { id = "0001" ; price = p ; side = "BID" ; price_map = [] }

    let to_string pb = Sexp.to_string pb

end;;

Finding it very strange why it doesnt want to bind :frowning:

When you add [@@deriving show] to a type declaration, it creates functions which are based on the name of the type you’re declaring.

So in your last message, the generated show function in PriceBook is called show_price_book_level, not show.

More precisely the rule for ppx_deriving is that if the type is named t, the function is show, otherwise for a type named x, the function is show_x.

That has worked thanks ! It prints as it should. Thank you :slight_smile:

Now, does ppx_deriving works for classes/objects as well or only ADT ?

You might be interested in this: How to print anything in OCaml - DEV Community 👩‍💻👨‍💻

3 Likes

thanks for the material. Will prove useful.

But it doesnt answer whether it works for classes in Ocaml? On another hand, is there any incentive to use classes/objects in Ocaml or its better to stick to fully functional approach?

Sorry, I realize that my post isn’t directly talking about printing in OCaml programs. I’ll update it. The context here is that once you’ve defined a pretty-printer, you can use the Format module to print it e.g.

let pp_obj = ...
let obj = object ... end
Format.printf "this is the object: %a\n" pp_obj obj

My post shows an example of defining a pretty-printer for an object.

(Side note: objects in OCaml can be fully functional. For better or worse, OCaml offers lots of non-redundant ways to structure programs and implement abstractions, that all fit together subdividing the design space. What differentiates objects from records is subtyping: one object type will be a special case of another object type if it offers more methods. Not so for records: when one record has strictly more fields than another, it cannot be used in place of the other.)

1 Like