-
Notifications
You must be signed in to change notification settings - Fork 158
Add Clash.Class.Convert
#2915
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add Clash.Class.Convert
#2915
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
I'd vote against naming it (I really want to make the silly pun |
6d3840f
to
d8f851c
Compare
Utilities for safely converting between various Clash number types.
d8f851c
to
25c7757
Compare
@clash-convertible@ is similar to the @convertible@ package in that it aims to | ||
facilitate conversions between different number types. It has two key differences: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To change because it isn't a separate package
{- | Conversions that are, based on their types, guaranteed to succeed. | ||
|
||
== __Laws__ | ||
A conversion is safe and total if a round trip conversion is guaranteed to be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"safe and total" is not terminology introduced earlier
{- | Conversions that may fail for some values. | ||
|
||
== __Laws__ | ||
A conversion is safe if a round trip conversion does not produce errors (also |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like the word "safe" here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM overall.
I'm going to accept this, because I cannot see anything really controversial in the sense that it would be hard to fix it later on.
Regarding the Convert
vs Convertible
question: I don't like either name, as they both suggest much freedom in terms of which types could be converted with the classes. Your documentation clearly restricts the setting to number types, which is in contrast to Convertible
for example, which is also open for conversion between custom non-number types. So maybe we should keep the users of that package happy by not producing any name clash at this point.
A name like changeNumType
would make it more clear to me what the purpose of this function is. Convert
, Convertible
, and convert
are just super generic names!
I still find it strange to consider BitVector
a number type. If we are going to have a discussion about this at some point, then I'd suggest to keep it out here for the time being. Adding it later is easy and the user still can define the instance locally, if necessary.
I don't like the fact that we need to have two classes in the end, but I get the point that the need for individual selection of constraints per instance introduces that requirement.
A conversion is safe and total if a round trip conversion is guaranteed to be | ||
lossless. I.e., | ||
|
||
> Just x == maybeConvert (convert @a @b x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a particular reason for mixing maybeConvert
and convert
in a single law? Wouldn't it make more sense to have a separate law per class, as a type not necessarily needs to be an instance of both?
|
||
> L.any isNothing (L.map (maybeConvert @a @b) [minBound ..]) | ||
|
||
Additionally, any implementation should be translatable to synthesizable RTL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mention synthesizable HDL in Clash.Class.Convert
. I'd suggest to keep the wording consistent.
-} | ||
class Convert a b where | ||
{- | Convert a supplied value of type @a@ to a value of type @b@. The conversion | ||
is guaranteed to succeed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The classical wording in legal specification documents usually is "must be guaranteed to succeed", as it is a user requirement, in case you like to stick with that.
instance (Convert (Unsigned 8) a) => Convert Word8 a where | ||
convert = convert . bitCoerce @_ @(Unsigned 8) | ||
|
||
instance (Convert (Signed 64) a) => Convert Int a where |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would either not add any instances for Word
and Int
, as their sizes are machine dependent, or the source of the respective conversions should be restricted to at most 32 bit, as this is what base fixes independently from the underlying machine.
instance (KnownNat n, KnownNat m, n <= m) => Convert (BitVector n) (BitVector m) where | ||
convert = resize | ||
|
||
instance (Convert (Unsigned 64) a) => Convert Word a where |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The RHS parenthesis are redundant here.
Just noting this, as hlint
won't complain about that, in case you prefer them being removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you have instances covering BitVector
, it might be also desirable to have instances converting from Bit
(Bool
). Something like a Bit
to Word8
conversion sounds reasonable to me.
On the other hand, we should ask ourselves whether BitVector
should be considered a number type? I'm still struggling with conceptual separation here, as if BitVector
is a number type, what distinguishes it from Unsigned
then in the end?
For the time being, if the input is an @XException@, then the output is too. | ||
This property might be relaxed in the future. | ||
-} | ||
maybeConvert :: a -> Maybe b |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally prefer the convention to call this function convertMaybe
(similar to readMaybe
in base), as the name then denotes the order of operation: "first convert and then put the potential result into the Maybe
container". maybeConvert
suggests to me that the function converts from a Maybe
as the input.
Another advantage of convertMaybe
is that TAB completion in the REPL will display both: convert
and convertMaybe
, as soon as you started typing c
o
n
... .
Thanks for the comments @kleinreact, I'll get to them, though I'm fairly time constrained at the moment :(. I think your comment about the name |
Somehow I feel Also, we could rewrite the laws such that the class can also be used for non-numbers. I think that would work? |
Utilities for safely converting between various Clash number types.
To discuss / investigate (but maybe not for this PR?)
1 <= n
constraints to every result where the result isIndex n
Index 0
has no inhabitants, so we cannot create it. On the other hand, we can of course just callerrorX "foo"
. I'm not sure whether I would say this violates the "guaranteed to succeed" rule.1 <= n
constraints to every argument where the argument isIndex n
convert
andmaybeConvert
will always returnXException
if given anXException
.BitVector 0
toIndex
/Unsigned
/Signed
? The HDL will happily do it, why wouldn't Clash simulation? Similarly, anyBitVector n
could be repacked to be partially undefined if converted to anotherBitVector m
wherem > n
.I'd push for kicking these questions to after this PR, because:
XException
behavior (but not introduce it).clash-prelude
.Still TODO:
Clash.Class.Convert
orClash.Class.Convertible
. The latter has some buy-in (?) from the community, given thatcovertible
already exists. On the other hand, this might make users think it isconvertible
, but it isn't.