Allow Partial constraint in instance declarations

Consider this code:

instance Semigroup MyType where
  append x = append' x

append' :: Partial => MyType -> MyType -> MyType

Currently, Partial constraint would prevent the instance from typechecking. But what if it was allowed to compile and Partial would just propagate to the call site? I think this behavior would be neat to have, since a lot of types have “almost lawful instances” and the parts where the laws break are exactly covered by partiality in the API (e.g. numeric overflows)

You might be just talking about Partial, and no others, but if you were advocating for a general feature of allowing instances to be defined with their own constraints and then propagating those constraints to the call site, then you’d be able to make Set a Functor:

instance Functor Set where
  map :: forall f a b. Ord b => (a -> b) -> Set a -> Set b
  map =

and you just need to apply the Ord b constraint wherever you used a Functor Set instance. Imagine I had the function

identityPlus :: forall f a. Functor f => f a -> f a
identityPlus x = 
  x # map (\a -> \(_ :: Unit) -> a) # map (\fn -> fn unit)

This should compile without problem. But now in some other module let’s say I try to call identityPlus with f = Set and a = Int. There’s no way for the compiler to know that it needs an Ord (Unit -> Int) instance unless it recompiled identityPlus.

So I don’t think it can work for a general feature to allow you to apply arbitrary constraints to arbitrary instances. Now it might work specifically for Partial (or maybe any nullary class?) if there was some special case there.

You have to propagate the Partial constraint.

instance Partial => Semigroup MyType where
  append x = append' x

append' :: Partial => MyType -> MyType -> MyType

tidy-codegen does this a lot for unsafe constructors.

1 Like