Hi Rich,
(dropping libvirt-devel)
On 09/30/21 13:53, Richard W.M. Jones wrote:
Also we don't currently try to find or rewrite /dev/disk/ paths
in
guest configuration files. The only rewriting that happens is for
/dev/[hs]d* block device filenames and a few others. The actual code
that does this is convert/convert_linux.ml:remap_block_devices
So I wouldn't over-think this. It's likely fine to identify such
devices and rewrite them as "/dev/cdrom", assuming that (I didn't
check) udev creates that symlink for any reasonably modern Linux. And
if there's more than one attached CD to the source, only convert the
first one and warn about but drop the others.
So I've started reading "convert/convert_linux.ml" in parallel with the
OCaml manual. (I dabbled for a few weeks in Haskell when everyone else
did a few years ago, so it's not 100% unfamiliar.)
Question:
let family =
match inspect.i_distro with
| "fedora"
| "rhel" | "centos" | "scientificlinux" |
"redhat-based"
| "oraclelinux" -> `RHEL_family
| "altlinux" -> `ALT_family
| "sles" | "suse-based" | "opensuse" -> `SUSE_family
| "debian" | "ubuntu" | "linuxmint" |
"kalilinux" -> `Debian_family
Here, RHEL_family, ALT_family, SUSE_family, Debian_family are not plain
constructors ("fixed" variants) [1] but polymorphic ones [2].
Why?
Was this done *only* in order so that an explicit type definition such
as
type os_family = RHEL_family | ALT_family | SUSE_family | Debian_family
could be avoided?
Based on my (incomplete understanding) of the ocaml docs, this looks
like a bad idea. We need no polymorphism here, and using a static type
(a fixed variant) is generally beneficial [3].
The following (minimally modified) definition at the OCaml REPL:
let family1 distro =
match distro with
| "fedora"
| "rhel" | "centos" | "scientificlinux" |
"redhat-based"
| "oraclelinux" -> `RHEL_family
| "altlinux" -> `ALT_family
| "sles" | "suse-based" | "opensuse" -> `SUSE_family
| "debian" | "ubuntu" | "linuxmint" |
"kalilinux" -> `Debian_family
| _ -> assert false;;
deduces the following type:
string -> [> `ALT_family | `Debian_family | `RHEL_family | `SUSE_family ] =
<fun>
with the nasty "[>" mark at the start (allowing for further type
refinement [2], which I think we don't need here). Conversely.
type os_family = RHEL_family | ALT_family | SUSE_family | Debian_family;;
let family2 distro =
match distro with
| "fedora"
| "rhel" | "centos" | "scientificlinux" |
"redhat-based"
| "oraclelinux" -> RHEL_family
| "altlinux" -> ALT_family
| "sles" | "suse-based" | "opensuse" -> SUSE_family
| "debian" | "ubuntu" | "linuxmint" |
"kalilinux" -> Debian_family
| _ -> assert false;;
(note the explicit os_family type definition, and the removal of the
backticks from the constructor names) comes back with the type:
val family2 : string -> os_family = <fun>
Basically converting a string to an enum constant.
So, what's the reason for the polymorphic variant?
[1]
https://ocaml.org/manual/coreexamples.html#s%3Atut-recvariants
[2]
https://ocaml.org/manual/polyvariant.html#sec48
[3]
https://ocaml.org/manual/polyvariant.html#s%3Apolyvariant-weaknesses
Thanks!
Laszlo