Suppose you declare two isomorphisms
showing :: (Read a, Show a) => Iso' String a
showing = iso read show
reading :: (Read a, Show a) => Iso' a String
reading = iso show read
They are unsafe, not every String will parse as an a.
That begs the question: Why those two aren't included in the library if enum = iso fromEnum toEnum
is?
It is just as unsafe and can't be prevented by the type system. They all shift the burden onto the programmer who has to ensure that the transformation will not break the isomorphism.
Quick example: under enum (+1) True
will throw an exception
Warning: speculative answer follows.
One difference between the two scenarios I can think of is that it is reasonably easy to clarify when toEnum
breaks down (quoting the lens docs: "this is only an isomorphism from the numeric range actually used"), while things are a fair bit murkier when it comes to read
. Another difference is that toEnum
is a method of Enum
(and one required in a minimal instance definition, at that), while read
isn't actually a method of Read
, and so is, in a sense, less essential (in fact, the base docs explicitly discourage its usage).
On a more general note, when it comes to choosing between exposing a bullet-proof interface and including useful but potentially unsafe or unlawful combinators, lens usually goes for the latter, making them available for those who wish to use them while acknowleding any potential issues in the documentation (or, in hairier cases, in function and module names). Given that lens is a very general purpose library, this approach can be seen as a non-opinionated choice -- in a context like this, it can be hard to compensate for the absence of combinators.
P.S.: It is also worth noting that lens encodes read
and show
through the _Show
prism. _Show
is equivalent to prism' show readMaybe
, and is safe with respect to the partiality of Read
-ing values.