2

I continually trick myself into thinking it'd be possible to somehow (at least imitate) having a typeclass, say, from base be arbitrarily constrained

(For all my searching I haven't found anything that satisfyingly addresses constraining type params from external libraries).


TL;DR How can I write the Arrow instance, even if I need to change MyArr, but must retain Typeable on its values?:

data MyArr a b where
  Arr :: (Typeable a, Typeable b) => (a -> b) -> MyArr a b

instance Arrow MyArr where ???

Consider this definition of Arrows:

class Arrow p where
  arr :: (a -> b) -> p a b

Sometimes I reeeeally wish it were

class Arrow p where
  arr :: (Typeable a, Typeable b) => ...

I would like to have that for the GADT I mentioned above; where my constructor carries a proof of its params Typeableity:

data MyArr a b where
  Arr :: (Typeable a, Typeable b) => (a -> b) -> MyArr a b

I cannot directly write an Arrow instance for that, because arr must be forall a b., and cannot generically conjure a proof of Typeable a. I think this is the crux of the problem.

If I could write my own class Arrow, I could make it work with ConstraintKinds using a kind family:

class Arrow p where
  type ArrowC p :: * -> Constraint
  arr :: (ArrowC p a, ArrowC p b) => ...

But since I'd like to remain compatible with proc notation, and other libraries, I cannot do that. So I keep thinking I could somehow define my Arrow's datatypes taking advantage of ConstraintKinds, or constraints, or, reify and reflection

data MyArr a b where
  Arr :: Dict (Typeable a) -> Dict (Typeable b) -> (a -> b) -> MyArr a b

But what Dict can I pass into the instance's definition of arr? or perhaps

data MyArr (k :: * -> Constraint) a b where
  Arr :: (k a, k b) => (a -> b) -> MyArr k a b

But dangit, that doesn't work either, because again, arr's a and b must be unconstrained

I've attempted tricks with having singleton values carry proofs, or constrain a and b by TypeFamilies, but alas, to no avail.

One of these options MUST be possible, right?

I have twisted myself in a ball of confusion that for months, I keep revisiting this problem, and tricking myself into thinking it's possible.

Josh.F
  • 3,666
  • 2
  • 27
  • 37
  • 4
    With the `Arrow` in `base`, it's utterly impossible. `arr :: (a -> b) -> p a b` breaks it. So does `first :: p a b -> p (a, c) (b, c)`. There are similar flexibly constrained classes in one or more packages, and you *might* be able to use them with `RebindableSyntax` (I'm not sure), but no way to really be compatible. – dfeuer Nov 11 '21 at 04:23
  • 3
    In case it helps, you shouldn't have any trouble with `Control.Category.Category`; those can be constrained all sorts of ways. One package is by @leftaroundabout: [`constrained-categories`](https://hackage.haskell.org/package/constrained-categories). – dfeuer Nov 11 '21 at 04:32
  • How much does GHC depend on `base`? I wonder if I could replace it? Or, I haven't played with Backpack but I was going to look into whether that could replace `Control.Arrow`, but, I imagine that's a dead end. Also I've considered `RebindableSyntax` but the haskell docs warn that it may not play nicely with Arrows specifically, and also, custom classes won't let me integrate with other Arrow libs per se (without a bit of elbow grease). Surprisingly a tough problem, no? I wonder if base classes should all have Typeable, since that's all I really need, but, that'd break a lot. Hmph. – Josh.F Nov 11 '21 at 06:29
  • 2
    Replacing `base` isn't currently possible, as far as I know, though some people have wanted that for years. Sticking `Typeable` all over fundamental classes would destroy a lot of the "free theorems" we rely on, as well as hurting performance in the usual case where we don't need those constraints. – dfeuer Nov 11 '21 at 06:52
  • 1
    Just reporting back, `constrained-categories` is surprisingly easy to use! The type sigs look intimidating, but, it's deceptively straightforward. Thanks! – Josh.F Nov 12 '21 at 04:55

0 Answers0