Hi Phage, I don’t mind at all.
First of all, with your headers function you are supplying a list with different types. The type-checker will complain that "hello"
is a string
when it is expecting an int
because of the 222
. OCaml lists are “homogeneous” i.e. all elements must have the same type.
Secondly, (which may just be a typo in the markdown) is that your type annotation for the function my_value
needs to use parentheses. You want to restrict s
to only Yaml.value
types. The way it is written right now will force the return type to be a Yaml.value
.
let my_value (s : Yaml.value) = match s with
| `String x -> x
| _ -> ""
Though not strictly necessary, when working with polymorphic variants (i.e. `String s
) the type-checker will say your function has type [> `String of string ] -> string
meaning it could take more than just the Yaml.value
variants as a parameter (the >
). By adding the type annotation we restrict that.
OCaml-yaml does some type-inference for us when parsing yaml files. Whenever we parse
api: 222
secret: "hello"
ocaml-yaml makes 222
a float
(wrapping it in `Float
) and "hello"
becomes a string
(wrapped in a `String
- note the quotation marks wouldn’t be necessary for this). There’s no way I know of without effectively wrapping values, to write a single function which extracts the primitive types because then what type would that function have (it would need to return floats, strings, lists etc.). So right now, once the above is fixed x3
is really `Float 222.
and when passed to my_value
this hits the catch-all case and we get ""
printed.
Solutions
So there are two possible solutions - (a) convert all Yaml values to one type (say a string
) or (b) write “unboxing” like functions for each Yaml value type and apply them based on your knowledge of what things should be. (a) is straightforward as ocaml-yaml can do it for you.
(* Raise exception version *)
let my_value = Yaml.to_string_exn
(* Provide a default version *)
let my_value s = match Yaml.to_string s with
| Ok s -> s
| _ -> ""
Note that by convention functions ending in _exn
raise an expection and without this ending they usually return an 'a t
for some t
(Result.t
or option
are common, ocaml-yaml uses the former).
The second option is to use unboxing function for each Yaml.value type and either provide defaults for the _
catch-all case or raise your on error. Here are two examples:
let unbox_float (f : Yaml.value) = match f with
| `Float f -> f
| _ -> 0.
let unbox_array (a : Yaml.value) = match a with
| `A f -> f
| _ -> []
But now there’s a problem, the list returned by unbox_array
contains “boxed” elements
Unfortunately without reboxing them to have a single type or through GADT magic (which I know little about) to make heterogeneous lists you just have to put up with this (which I don’t think is a bad thing).
Hope this helps – I feel like this is a great example of how OCaml’s type system feels a little “restricting” but is actually guiding you in writing safer and less error-prone code by having to handle the types and provide cases for everything that you have to.