├── Makefile ├── README.md ├── containers.dot ├── data.dot └── images ├── containers.png └── data.png /Makefile: -------------------------------------------------------------------------------- 1 | build_dir = ./images 2 | outputs = $(build_dir)/containers.png $(build_dir)/data.png 3 | 4 | font = "JetBrainsMono" 5 | node_color = "\#f5f7ff" 6 | line_color = "\#5e6687" 7 | 8 | all: $(outputs) 9 | 10 | $(outputs): $(build_dir)/%.png: %.dot 11 | circo -Gfontname=$(font) -Nfontname=$(font) -Efontname=$(font) -Nstyle="filled" \ 12 | -Nfillcolor=$(node_color) -Ncolor=$(line_color) -Nfontcolor=$(line_color) \ 13 | -Ecolor=$(line_color) -Efontcolor=$(line_color) \ 14 | -Tsvg $< > tmp.svg && convert -density 200 tmp.svg $@ 15 | rm tmp.svg 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Converting between standard OCaml types 2 | 3 | The OCaml standard library provides many convertion functions of the form 4 | `Foo.{to,of}_bar` between its various types. These are incomplete – and somewhat 5 | arbitrary – so it's often necessary to go via intermediate types to achieve the 6 | convertion you want. (Either that or roll your own with a `fold` or `iter` 7 | function.) 8 | 9 | What's worse, you may need to mix forwards and backwards conversions (`to_` and 10 | `of_` style respectively), requiring you to search both forwards and backwards 11 | in the documentation to get the conversion you need. For example, consider the 12 | case of `('k, 'v) Hashtbl.t -> ('k * 'v) list`: 13 | 14 | 1. Looking in the docs for [`Hashtbl`][hashtbl-doc], we see that there's no 15 | direct conversion to an association list. We might give up and build our own 16 | one with `Hashtbl.fold`. On OCaml 4.07+, we have `Hashtbl.to_seq : ('k, 'v) Hashtbl.t -> ('k, 'v) Seq.t`, so let's start by using that. 17 | 18 | 1. We look in the docs for [`Seq`][seq-doc], and find no conversions. Once again, 19 | we could use get distracted and use `Seq.fold_left` to build our own. Or, we 20 | could start searching backwards from the type we want... 21 | 22 | 1. In the docs for [`List`][list-doc], we find the function `List.of_seq : 'a Seq.t -> 'a List.t`. Now we can complete the chain: 23 | 24 | ```ocaml 25 | let hashtbl_to_list h = Hashtbl.to_seq h |> List.of_seq 26 | ``` 27 | 28 | Rather than keeping the entire Stdlib documentation open in browser tabs, it can 29 | be helpful to view all of the available conversions as a directed graph. That's 30 | what this repository provides. (These graphs show the conversions available on 31 | OCaml 4.11.0; your mileage may vary.) 32 | 33 | [hashtbl-doc]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Hashtbl.html 34 | [seq-doc]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/Seq.html 35 | [list-doc]: https://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html 36 | 37 | ## Container types 38 | 39 | ![Conversions between containers in OCaml](./images/containers.png) 40 | 41 | Take-aways: 42 | 43 | - The common containers are ordered by: 44 | 45 | - `Array` ⊏ `List` ⊏ `Seq` 46 | - `Buffer` ⊏ `Bytes` ⊏ `String` ⊏ `Seq` 47 | - {`Hashtbl`, `Map`, `Queue`, `Set`, `Stack`} ⊏ `Seq` 48 | 49 | - To convert between containers `Foo.t` → `Bar.t`, it's usually necessary to go 50 | via `Seq.t` with `Foo.to_seq >> Bar.of_seq`. 51 | 52 | - There's no direct conversion between `float Array.t` and `Float.Array.t`. 53 | 54 | ## Data types 55 | 56 | ![Conversions between data in OCaml](./images/data.png) 57 | 58 | Take-aways: 59 | 60 | - The numeric modules are ordered by `Int64` ⊏ `Nativeint` ⊏ `Int32` ⊏ {`Int`, 61 | `Float`}. When coercing between them, you should check modules in this order. 62 | -------------------------------------------------------------------------------- /containers.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | graph [pad="0.5", nodesep="0.25", ranksep="0.85"]; 3 | node [color="#5e6687", shape="box"]; 4 | edge [color="#5e6687", arrowhead="empty"]; 5 | 6 | subgraph cluster_legend { 7 | node [fillcolor=none, shape=plaintext] 8 | key [label=< 9 | 10 | 11 |
Foo.to_bar
Bar.of_foo
>] 12 | key2 [label=< 13 | 14 | 15 |
 
 
>] 16 | key:i1:e -> key2:i1:w [color=green] 17 | key:i2:e -> key2:i2:w [color=red] 18 | } 19 | 20 | 21 | // List 22 | List -> Seq [color=green] 23 | Seq -> List [color=red] 24 | 25 | // Array 26 | Array -> List [color=green] 27 | Array -> Seq [color=green] 28 | List -> Array [color=red] 29 | Seq -> Array [color=red] 30 | 31 | // Float.Array 32 | "Float.Array" -> List [color=green] 33 | "Float.Array" -> Seq [color=green] 34 | List -> "Float.Array" [color=red] 35 | Seq -> "Float.Array" [color=red] 36 | 37 | // Hashtbl 38 | Hashtbl -> Seq [color=green] 39 | Seq -> Hashtbl [color=red] 40 | 41 | // Map 42 | Map -> Seq [color=green] 43 | Seq -> Map [color=red] 44 | 45 | // Set 46 | Set -> Seq [color=green] 47 | List -> Set [color=red] 48 | Seq -> Set [color=red] 49 | 50 | // Bytes 51 | Bytes -> Seq [color=green] 52 | Bytes -> String [color=green] 53 | String -> Bytes [color=red] 54 | Seq -> Bytes [color=red] 55 | 56 | // Buffer 57 | Buffer -> Bytes [color=green] 58 | Buffer -> Seq [color=green] 59 | Seq -> Buffer [color=red] 60 | 61 | // Bigarray 62 | Array -> Bigarray [color=red] 63 | 64 | // Stack 65 | Stack -> Seq [color=green] 66 | Seq -> Stack [color=red] 67 | 68 | // Stream 69 | List -> Stream [color=red] 70 | String -> Stream [color=red] 71 | Bytes -> Stream [color=red] 72 | 73 | // String 74 | String -> Seq [color=green] 75 | Seq -> String [color=red] 76 | 77 | // Result 78 | Result -> List [color=green] 79 | Result -> Option [color=green] 80 | Result -> Seq [color=green] 81 | 82 | // Queue 83 | Queue -> Seq [color=green] 84 | Seq -> Queue [color=red] 85 | 86 | // Option 87 | Option -> Result [color=green] 88 | Option -> List [color=green] 89 | Option -> Seq [color=green] 90 | } 91 | -------------------------------------------------------------------------------- /data.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | graph [pad="0.5", nodesep="0.25", ranksep="0.85"]; 3 | node [color="#5e6687", shape="box"]; 4 | edge [color="#5e6687", arrowhead="empty", arrowtail="empty"]; 5 | 6 | subgraph cluster_legend { 7 | node [fillcolor=none, shape=plaintext] 8 | key [label=< 9 | 10 | 11 | 12 |
Foo.to_bar
Bar.of_foo
(partial)
>] 13 | key2 [label=< 14 | 15 | 16 | 17 |
 
 
 
>] 18 | key:i1:e -> key2:i1:w [color=green] 19 | key:i2:e -> key2:i2:w [color=red] 20 | key:i3:e -> key2:i3:w [style=dashed] 21 | } 22 | 23 | // Int & Float 24 | Int -> Float [color=green, dir=both] 25 | Float -> Int [color=red, dir=both] 26 | 27 | // Int32 28 | Int32 -> Int [color=green] 29 | Int32 -> Float [color=green] 30 | Int -> Int32 [color=red] 31 | Float -> Int32 [color=red] 32 | 33 | // Int64 34 | Int64 -> Int [color=green] 35 | Int64 -> Int32 [color=green] 36 | Int64 -> Nativeint [color=green] 37 | Int64 -> Float [color=green] 38 | Int -> Int64 [color=red] 39 | Int32 -> Int64 [color=red] 40 | Nativeint -> Int64 [color=red] 41 | Float -> Int64 [color=red] 42 | 43 | // Nativeint 44 | Nativeint -> Int [color=green] 45 | Nativeint -> Int32 [color=green] 46 | Nativeint -> Float [color=green] 47 | Int -> Nativeint [color=red] 48 | Int32 -> Nativeint [color=red] 49 | Float -> Nativeint [color=red] 50 | 51 | // Char 52 | Char -> Int [color=green] 53 | Int -> Char [style=dashed, color=red] 54 | 55 | // Bool 56 | Bool -> Int [color=green] 57 | Bool -> Float [color=green] 58 | 59 | // Bytes 60 | 61 | // Uchar 62 | Uchar -> Int [color=green] 63 | Uchar -> Char [style=dashed, color=green] 64 | Int -> Uchar [style=dashed, color=red] 65 | Char -> Uchar [color=red] 66 | 67 | // Unit 68 | } 69 | -------------------------------------------------------------------------------- /images/containers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/craigfe/ocaml-stdlib-type-conversions/9b257f90f4b84bc3a342a6656b25f63aeaf1beba/images/containers.png -------------------------------------------------------------------------------- /images/data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/craigfe/ocaml-stdlib-type-conversions/9b257f90f4b84bc3a342a6656b25f63aeaf1beba/images/data.png --------------------------------------------------------------------------------