Open raehik opened 1 year ago
SizeGreaterThan
, SizeLessThan
and SizeEqualTo
all have a Predicate1
. (The Predicate
instance can be recovered as validate = validate1
.) Ascending
and Descending
don't, because they operate on the values inside.
Testing over at https://github.com/raehik/refined/tree/refined1 .
It honestly feels like you could do something with Existentials and/or Quantified constraints here, without a new class and type.
I think that would work for defining Predicate1
s. But it doesn't allow me to recover Functor/Traversable for types refined with such predicates. I still have to derive it myself each time using reallyUnsafeRefine and frantically reminding myself "the predicate has no access to a
".
Would type Refined p = Refined1 p Identity
with a few tweaked definitions work? (I feel it won't, but worth a mention.)
I'm not sure how I would solve this with existentials. Also, the new class and type are fairly lightweight (the various coercions are the most work). I had an inkling that Aeson did something similar with FromJSON1, so felt empowered.
I don't have a good MWE for this, but it did fix my issue. Given a type
newtype AW32Pairs f a = AW32Pairs
{ unAW32Pairs :: CountPrefixed Word8 [] (f a) }
I can now safely derive Functor
and Traversable
s like
instance Functor f => Functor (AW32Pairs f) where
fmap f = AW32Pairs . fmap (fmap f) . unAW32Pairs
(I didn't check this, adapted from a different type.)
This is certainly useful for code which works with refinement types for parsing/serializing (which they are really good for).
I'm not sure how I'd do this another way-- I think I need a concrete type like Refined1
so I can give it the relevant instances that Refined
can't have. It would be nice not to repeat all the constructors and destructors though. This is at the bottom of a stack of libs I write, so I might for now release it as an extension lib.
I'd need time to sit down and think about this and your other ideas. This does seem useful though.
I think we can add some kind of namespace like Refined.Experimental.*, where we can add this. I'd like to iterate on a design. I've been holding off because I couldn't think of a good one.
I think we can add some kind of namespace like Refined.Experimental.*, where we can add this.
That would be neat. I would have a suggestion for such a namespace: a predicate simplifier. I wrote a naive one for rerefined at Rerefined.Simplify. It can reduce Boolean identity laws and do a tiny bit of local reasoning about numeric relational predicates. One could use it to catch trivially-simplifiable predicates at compilation, which are probably better avoided.
I have the following predicate which asserts that the number of elements in some
Foldable f => f a
can be safely represented by the given type:This works nicely. However, I find myself writing a bunch of boilerplate
Functor
/Traversable
instances for newtypes over instantiations off a
, which inevitably include doing areallyUnsafeRefine
at the end. This is safe, because I assert the predicate usingFoldable f
-- it's not really a predicate ona
at all.We could define another refinement type for
Type -> Type
-kinded types, wherea
can't be accessed:Now I can get refinements on functors & list-likes with less boilerplate and no
reallyUnsafeRefine
.What do you think? Is this useful, enough to add to the library?