Hey, I wonder how one can get it working with a Map. For example,
module M = Map.Make(Int)[@@deriving of_yojson]
let m = M.empty |> M.add 1 2 |> M.add 3 4 |> M.add 5 6
let s =
[%yojson_of: int M.t] m
|> Yojson.Safe.to_string;;
module M = struct
module Tmp = Map.Make(Int)
include Tmp
let yojson_of_t m = Tmp.bindings m |> [%yojson_of: (int * int) list]
end
let m = M.empty |> M.add 1 2 |> M.add 3 4 |> M.add 5 6
let s =
[%yojson_of: M.t] m
|> Yojson.Safe.to_string
You can do that, or you can expand the module at the functor level to keep its versatility. Example of expanding Set:
module Set = struct
module type S = sig
include Set.S
val t_of_yojson : Yojson.Safe.t -> t
val yojson_of_t : t -> Yojson.Safe.t
end
module Make(O:OrderedType) = struct
include Set.Make(O)
let t_of_yojson (json:Yojson.Safe.t) =
list_of_yojson O.t_of_yojson json |> of_list
let yojson_of_t (t:t) =
to_list t |> yojson_of_list O.yojson_of_t
end
end
module M = struct
module Tmp = Map.Make(Int)
include Tmp
let yojson_of_t m = Tmp.bindings m |> [%yojson_of: (int * int) list]
end
let m =
M.empty
|> M.add 1 2
|> M.add 3 4
|> M.add 5 6
let s =
m
|> [%yojson_of: M.t]
|> Yojson.Safe.to_string
module Nested = struct
module Tmp = Map.Make(Int)
include Tmp
let yojson_of_t m = Tmp.bindings m |> [%yojson_of: (int * M.t) list]
end
let m =
M.empty
|> M.add 1 2
|> M.add 3 4
|> M.add 5 6
let s =
Nested.empty
|> Nested.add 1 m
|> Nested.add 2 m
|> [%yojson_of: Nested.t]
|> Yojson.Safe.to_string
(* expand at Functor level *)
module type OrderedType = sig
type t
val compare : t -> t -> int
val t_of_yojson : Yojson.Safe.t -> t
val yojson_of_t : t -> Yojson.Safe.t
end
module Set = struct
module type S = sig
include Set.S
val t_of_yojson : Yojson.Safe.t -> t
val yojson_of_t : t -> Yojson.Safe.t
end
module Make(O:OrderedType) = struct
include Set.Make(O)
let t_of_yojson (json:Yojson.Safe.t) =
list_of_yojson O.t_of_yojson json |> of_list
let yojson_of_t (t:t) =
to_seq t |> List.of_seq |> yojson_of_list O.yojson_of_t
end
end
module Int = struct
type t = int [@@deriving yojson]
let compare = Stdlib.compare
end
module M = Set.Make(Int)
let s =
M.empty
|> M.add 1
|> M.add 2
|> M.add 3
|> [%yojson_of: M.t]
|> Yojson.Safe.to_string
This feels hacky, but for a one-off [%yojson_of] use you just need the right functions with the right names in scope, not even the types:
let yojson_of_int_map yojson_of_value map =
let module M = Map.Make (Int) in
[%yojson_of: (int * value) list] (M.bindings map)
let int_map_to_string yojson_of_value map =
Yojson.Safe.to_string ([%yojson_of: value int_map] map)
The types value and 'a int_map don’t actually exist here, but the PPX produces the right calls to yojson_of_value and yojson_of_int_map yojson_of_value anyway.
However, using an actual alias type 'a int_map = 'a Map.Make (Int).t would also enable other types to use [@@deriving yojson_of] without redefining a full Map module. You can then hide those aliases from your module signature if you want.
I did this (just as you did) for both maps and sets, and for yojson and show. And for the latter, I wanted both pp and pp_hum. It’s a pity there’s no way to “add” these extra functions, but then, I guess once you make the new extended functors, you can use them all over the place, so it’s not like there’s a giant expenditure of effort.
Hey, Chet, can you share your implementation for a Map: I can’t figure out how to deal with the values being stored:
type t
val compare : t -> t -> int
val t_of_yojson : Yojson.Safe.t -> t
val yojson_of_t : t -> Yojson.Safe.t
end
module Map = struct
module type S = sig
include Map.S
val t_of_yojson : Yojson.Safe.t -> 'a t
val yojson_of_t : 'a t -> Yojson.Safe.t
end
module Make(O:OrderedType) = struct
include Map.Make(O)
let t_of_yojson (json:Yojson.Safe.t) =
list_of_yojson O.t_of_yojson json |> List.to_seq |> of_seq
let yojson_of_t (t:t) =
to_seq t |> List.of_seq |> yojson_of_list O.yojson_of_t
end
end;;
the error message:
Error: This expression has type key Seq.t = unit -> key Seq.node
but an expression was expected of type
(key * 'a) Seq.t = unit -> (key * 'a) Seq.node
Type key is not compatible with type key * 'a ```
N.B.: this uses my camlp5-based pa_ppx PPX rewriters, not the standard ppxlib PPX infrastructure. But these are the bog-standard ones, so the code should work with the standard rewriters. Looking at the code, I see that I didn’t implement to_yojson. I don’t remember if this is because there was a problem, or b/c I didn’t need it.
I never use utop, but also, since all of my code is based on camlp5, the mechanism whereby pretty printing in toplevels happens is probably different from how it works with the normal PPX infrastructure. generally, I use #install_printer And that has worked perfectly fine.
Following this thread, I tried to have this piece of code working but I am stuck into this compilation error. I feel the [@@deriving yojson] is not working as expected, but I can’t manage to solve it.:
File "bin/main.ml", lines 29-32, characters 25-3:
29 | .........................struct
30 | type t = int [@@deriving yojson]
31 | let compare = Stdlib.compare
32 | end
Error: Signature mismatch:
...
The value t_of_yojson is required but not provided
File "bin/main.ml", line 7, characters 2-38: Expected declaration
The value yojson_of_t is required but not provided
File "bin/main.ml", line 8, characters 2-38: Expected declaration
open Ppx_yojson_conv_lib.Yojson_conv.Primitives
open Ppx_yojson_conv_lib.Yojson_conv
module type OrderedType = sig
type t
val compare : t -> t -> int
val t_of_yojson : Yojson.Safe.t -> t
val yojson_of_t : t -> Yojson.Safe.t
end
module Map = struct
module type S = sig
include Map.S
val t_of_yojson : Yojson.Safe.t -> 'a t
val yojson_of_t : 'a t -> Yojson.Safe.t
end
module Make(O:OrderedType) = struct
include Map.Make(O)
let t_of_yojson (json:Yojson.Safe.t) =
let conv = pair_of_yojson O.t_of_yojson (fun x -> x)in
of_list (list_of_yojson conv json)
let yojson_of_t (t:'a t) =
yojson_of_list (yojson_of_pair O.yojson_of_t (fun x->x)) (to_list t)
end
end
module Int:OrderedType = struct
type t = int [@@deriving yojson]
let compare = Stdlib.compare
end
module IntMap = Map.Make(MyInt)
That doesn’t look like the code that you compiled above? There are no ‘@@deriving’ attributes in it? Can you share a complete example? E.g., maybe put it up on GitHub and share a link ?
Typically when I get into these sorts of problems, I will look in the “log” file in the Dune build directory and find the compile directives that cause the problem, and add “-dsource” to get the source code post-PPX, and then I can try to compile that and debug my way to understanding what went wrong.
It’s been a really long time, but maybe the problem is that when the type is “t”, you don’t get “t_of_yojson”, but instead “of_yojson”. I’m just guessing – my laptop is in a bit of a shambles, so I can’t run emacs well enough to tool around in sources to check. But you can verify that by using “-dsource”.
You are a good guesser ! I have checked and that’s indeed what I have seen… plus it looks like to_yojson and of_yojson are recursive functions… “-dsource” helped a lot for that. Thank you, I am now digging further.
Note that you can do dune describe pp path/to/file.ml to get the preprocessed version of the file without having the dig in the logs.
From what you are saying now it sounds like you have switched from ppx_yojson_conv to ppx_deriving_yojson at some point during development. The names of the functions they generate are different. If you want to work with the Janestreet ecosystem you should probably use ppx_yojson_conv.
Thank you @Khady for the tip on dune : dune describe pp path/to/file.ml I will try that as well. -dsource worked without digging into the logs (printed on the output, but maybe just because it was not compiling…)
Good hint as well about ppx_yojson_conv versus ppx_deriving_yojson. I must confess that I wasn’t really aware of those two flavors of the same lib, hence neither of a mix in my code. That’s what happens when you learn things alone. You get information from various sources but never get the whole picture you would get from an expert/teacher… That will help too !